Skip to content

Commit

Permalink
chore: Property filter custom form header improvement (#2835)
Browse files Browse the repository at this point in the history
  • Loading branch information
pan-kot authored Oct 9, 2024
1 parent 36b33be commit 5b0e892
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 28 deletions.
6 changes: 6 additions & 0 deletions pages/property-filter/custom-forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
grid-template-columns: auto;
gap: awsui.$space-scaled-s;
}

@media (min-width: 688px) {
.date-time-form {
grid-template-columns: repeat(2, minmax(100px, max-content));
}
}

.multiselect-form {
min-width: 200px;
max-width: 250px;
}
45 changes: 35 additions & 10 deletions pages/property-filter/custom-forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, { useEffect, useState } from 'react';
import { DatePicker, FormField, RadioGroup, TimeInput, TimeInputProps } from '~components';
import Calendar, { CalendarProps } from '~components/calendar';
import DateInput from '~components/date-input';
import EmbeddedMultiselect from '~components/multiselect/embedded';
import InternalMultiselect from '~components/multiselect/internal';
import { ExtendedOperatorFormProps } from '~components/property-filter/interfaces';

Expand Down Expand Up @@ -220,11 +221,12 @@ function formatTimezoneOffset(isoDate: string, offsetInMinutes?: number) {

const allOwners = [...new Set(allItems.map(({ owner }) => owner))];

export function OwnerMultiSelectForm({ value, onChange }: ExtendedOperatorFormProps<string[]>) {
export function OwnerMultiSelectForm({ value, onChange, filter }: ExtendedOperatorFormProps<string[]>) {
value = value && Array.isArray(value) ? value : [];
return (
<FormField stretch={true}>
<InternalMultiselect

if (typeof filter !== 'undefined') {
return (
<EmbeddedMultiselect
options={allOwners.map(owner => ({ value: owner, label: owner }))}
selectedOptions={value.map(owner => ({ value: owner, label: owner })) ?? []}
onChange={event =>
Expand All @@ -234,14 +236,37 @@ export function OwnerMultiSelectForm({ value, onChange }: ExtendedOperatorFormPr
.filter((value): value is string => typeof value !== 'undefined')
)
}
filteringText={filter}
statusType="finished"
filteringType="none"
expandToViewport={true}
keepOpen={true}
hideTokens={false}
inlineTokens={true}
filteringType="auto"
empty="No options available"
noMatch="No options matched"
/>
</FormField>
);
}

return (
<div className={styles['multiselect-form']}>
<FormField stretch={true}>
<InternalMultiselect
options={allOwners.map(owner => ({ value: owner, label: owner }))}
selectedOptions={value.map(owner => ({ value: owner, label: owner })) ?? []}
onChange={event =>
onChange(
event.detail.selectedOptions
.map(({ value }) => value)
.filter((value): value is string => typeof value !== 'undefined')
)
}
statusType="finished"
filteringType="none"
expandToViewport={true}
keepOpen={true}
hideTokens={false}
inlineTokens={true}
/>
</FormField>
</div>
);
}

Expand Down
13 changes: 9 additions & 4 deletions src/multiselect/embedded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import DropdownFooter from '../internal/components/dropdown-footer/index.js';
import ScreenreaderOnly from '../internal/components/screenreader-only/index.js';
import { useFormFieldContext } from '../contexts/form-field';
import DropdownFooter from '../internal/components/dropdown-footer';
import ScreenreaderOnly from '../internal/components/screenreader-only';
import { useUniqueId } from '../internal/hooks/use-unique-id';
import { SomeRequired } from '../internal/types';
import PlainList from '../select/parts/plain-list';
Expand All @@ -30,14 +31,15 @@ export type EmbeddedMultiselectProps = SomeRequired<
| 'finishedText'
| 'errorText'
| 'recoveryText'
| 'empty'
| 'noMatch'
>,
'options' | 'selectedOptions' | 'filteringType' | 'statusType' | 'controlId'
'options' | 'selectedOptions' | 'filteringType' | 'statusType'
> & { filteringText?: string };

const EmbeddedMultiselect = React.forwardRef(
(
{
controlId,
options,
filteringType,
ariaLabel,
Expand All @@ -49,8 +51,11 @@ const EmbeddedMultiselect = React.forwardRef(
}: EmbeddedMultiselectProps,
externalRef: React.Ref<MultiselectProps.Ref>
) => {
const formFieldContext = useFormFieldContext(restProps);
const ariaLabelId = useUniqueId('multiselect-ariaLabel-');
const footerId = useUniqueId('multiselect-footer-');
const selfControlId = useUniqueId('multiselect-trigger-');
const controlId = formFieldContext.controlId ?? selfControlId;

const multiselectProps = useMultiselect({
options,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { act, render } from '@testing-library/react';

import { KeyCode } from '@cloudscape-design/test-utils-core/dist/utils.js';

import Input from '../../../lib/components/input';
import PropertyFilter from '../../../lib/components/property-filter';
import { FilteringProperty, PropertyFilterProps, Ref } from '../../../lib/components/property-filter/interfaces.js';
import createWrapper, { PropertyFilterWrapper } from '../../../lib/components/test-utils/dom';
Expand Down Expand Up @@ -54,12 +55,26 @@ describe('extended operators', () => {
</button>
),
},
{
operator: '=',
form: ({ value, onChange }) => (
<Input name="" value={value} onChange={({ detail }) => onChange(detail.value)} />
),
},
],
propertyLabel: 'index',
groupValuesLabel: 'index value',
};
const extendedOperatorProps = { filteringProperties: [indexProperty] };

test('property label is used to annotate custom form field', () => {
const { propertyFilterWrapper: wrapper } = renderComponent(extendedOperatorProps);
wrapper.setInputValue('index =');
expect(
wrapper.findDropdown()!.findOpenDropdown()!.findInput()!.findNativeInput()!.getElement()
).toHaveAccessibleName('index value');
});

test('property filter renders operator form instead of options list', () => {
const { propertyFilterWrapper: wrapper } = renderComponent(extendedOperatorProps);
wrapper.setInputValue('index >');
Expand Down
11 changes: 8 additions & 3 deletions src/property-filter/property-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import React from 'react';

import InternalButton from '../button/internal';
import InternalFormField from '../form-field/internal';
import { FormFieldContext } from '../internal/context/form-field-context';
import { useUniqueId } from '../internal/hooks/use-unique-id';
import { I18nStringsInternal } from './i18n-utils';
import { ComparisonOperator, ExtendedOperatorForm, InternalFilteringProperty, InternalToken } from './interfaces';

Expand All @@ -25,12 +26,16 @@ export function PropertyEditorContent<TokenValue = any>({
onChange: (value: null | TokenValue) => void;
operatorForm: ExtendedOperatorForm<TokenValue>;
}) {
const labelId = useUniqueId();
return (
<div className={styles['property-editor']}>
<div className={styles['property-editor-header']} id={labelId}>
{property.groupValuesLabel}
</div>
<div className={styles['property-editor-form']}>
<InternalFormField label={property.groupValuesLabel}>
<FormFieldContext.Provider value={{ ariaLabelledby: labelId }}>
{operatorForm({ value, onChange, operator, filter })}
</InternalFormField>
</FormFieldContext.Provider>
</div>
</div>
);
Expand Down
21 changes: 10 additions & 11 deletions src/property-filter/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,21 @@ $operator-field-width: 120px;
}

.property-editor {
margin-block: awsui.$space-xxs;
margin-inline: awsui.$space-xxs;
padding-block: awsui.$space-m;
padding-inline: awsui.$space-m;
overflow-y: auto;

&-field-property {
/* used in test-utils */
}
&-header {
@include styles.default-text-style;
font-weight: bold;

&-field-operator {
margin-block-start: awsui.$space-scaled-l;
padding-block-start: awsui.$space-s;
padding-block-end: awsui.$space-xxs;
padding-inline: awsui.$space-s;
}

&-field-value {
margin-block-start: awsui.$space-scaled-l;
&-form {
padding-block-start: awsui.$space-xxs;
padding-block-end: awsui.$space-s;
padding-inline: awsui.$space-s;
}

&-cancel {
Expand Down

0 comments on commit 5b0e892

Please sign in to comment.