Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pages/property-filter/common-props.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { PropertyFilterProps } from '~components/property-filter';
import { I18nStringsExt } from '~components/property-filter/i18n-utils';
import { I18nStringsTokenGroups } from '~components/property-filter/interfaces';

import {
DateForm,
Expand Down Expand Up @@ -146,7 +146,7 @@ export const labels = {
filteringPlaceholder: 'Search',
};

export const i18nStrings: PropertyFilterProps.I18nStrings & I18nStringsExt = {
export const i18nStrings: PropertyFilterProps.I18nStrings = {
dismissAriaLabel: 'Dismiss',

groupValuesText: 'Values',
Expand Down Expand Up @@ -184,7 +184,9 @@ export const i18nStrings: PropertyFilterProps.I18nStrings & I18nStringsExt = {

formatToken,
removeTokenButtonAriaLabel: token => `Remove token, ${formatToken(token)}`,
};

export const i18nStringsTokenGroups: I18nStringsTokenGroups = {
groupEditAriaLabel: group => `Edit group with ${group.tokens.length} tokens`,
tokenEditorTokenActionsAriaLabel: token => `Filter remove actions for ${formatToken(token)}`,
tokenEditorTokenRemoveAriaLabel: token => `Remove filter, ${formatToken(token)}`,
Expand Down
5 changes: 3 additions & 2 deletions pages/property-filter/custom-forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +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 Multiselect from '~components/multiselect';
import InternalMultiselect from '~components/multiselect/internal';
import { ExtendedOperatorFormProps } from '~components/property-filter/interfaces';

import { allItems } from './table.data';
Expand Down Expand Up @@ -224,7 +224,7 @@ export function OwnerMultiSelectForm({ value, onChange }: ExtendedOperatorFormPr
value = value && Array.isArray(value) ? value : [];
return (
<FormField stretch={true}>
<Multiselect
<InternalMultiselect
options={allOwners.map(owner => ({ value: owner, label: owner }))}
selectedOptions={value.map(owner => ({ value: owner, label: owner })) ?? []}
onChange={event =>
Expand All @@ -235,6 +235,7 @@ export function OwnerMultiSelectForm({ value, onChange }: ExtendedOperatorFormPr
)
}
expandToViewport={true}
inlineTokens={true}
/>
</FormField>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ const defaultProps: Omit<TokenEditorProps, 'i18nStrings'> = {
filteringOptions: [],
onSubmit: () => {},
onDismiss: () => {},
standaloneTokens: [],
onChangeStandalone: () => {},
tokensToCapture: [],
onTokenCapture: () => {},
onTokenRelease: () => {},
tempGroup: [{ property: null, operator: ':', value: 'search text' }],
onChangeTempGroup: () => {},
};
Expand Down Expand Up @@ -118,37 +119,35 @@ const tokenPermutations = createPermutations<Partial<TokenEditorProps>>([
{ property: nameProperty, operator: '=', value: 'Jack' },
],
],
standaloneTokens: [
tokensToCapture: [
[
{ property: dateProperty, operator: '=', value: new Date('2020-01-01') },
{ property: dateProperty, operator: '=', value: new Date('2020-01-02') },
{ property: dateProperty, operator: '=', value: new Date('2020-01-03') },
{ property: dateProperty, operator: '=', value: new Date('2020-01-04') },
{ property: dateProperty, operator: '=', value: new Date('2020-01-05') },
{ standaloneIndex: 0, property: dateProperty, operator: '=', value: new Date('2020-01-01') },
{ standaloneIndex: 1, property: dateProperty, operator: '=', value: new Date('2020-01-02') },
{ standaloneIndex: 2, property: dateProperty, operator: '=', value: new Date('2020-01-03') },
{ standaloneIndex: 3, property: dateProperty, operator: '=', value: new Date('2020-01-04') },
{ standaloneIndex: 4, property: dateProperty, operator: '=', value: new Date('2020-01-05') },
],
],
},
]);

function TokenEditorStateful(props: Omit<TokenEditorProps, 'i18nStrings'>) {
const [tempGroup, setTempGroup] = useState(props.tempGroup);
const [standaloneTokens, setStandaloneTokens] = useState(props.standaloneTokens);
const capturedTokenIndices = tempGroup.map(token => token.standaloneIndex).filter(Boolean);
const tokensToCapture = props.tokensToCapture.filter((_, index) => !capturedTokenIndices.includes(index));
const i18nStringsInternal = usePropertyFilterI18n(i18nStrings);
return (
<TokenEditor
{...props}
i18nStrings={i18nStringsInternal}
standaloneTokens={standaloneTokens}
onChangeStandalone={setStandaloneTokens}
tokensToCapture={tokensToCapture}
tempGroup={tempGroup}
onChangeTempGroup={setTempGroup}
onDismiss={() => {
setTempGroup(props.tempGroup);
setStandaloneTokens(props.standaloneTokens);
}}
onSubmit={() => {
setTempGroup(props.tempGroup);
setStandaloneTokens(props.standaloneTokens);
}}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Button from '~components/button';
import Header from '~components/header';
import I18nProvider from '~components/i18n';
import messages from '~components/i18n/messages/all.en';
import PropertyFilter from '~components/property-filter';
import PropertyFilter from '~components/property-filter/internal';
import SplitPanel from '~components/split-panel';
import Table from '~components/table';

Expand Down Expand Up @@ -97,8 +97,11 @@ export default function () {
filteringOptions={filteringOptions}
virtualScroll={true}
countText={`${items.length} matches`}
enableTokenGroups={true}
expandToViewport={true}
filteringEmpty="No properties"
customGroupsText={[]}
disableFreeTextFiltering={false}
/>
}
columnDefinitions={columnDefinitions.slice(0, 2)}
Expand Down
2 changes: 2 additions & 0 deletions pages/property-filter/token-editor.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
columnDefinitions,
filteringProperties as commonFilteringProperties,
i18nStrings,
i18nStringsTokenGroups,
labels,
} from './common-props';

Expand All @@ -27,6 +28,7 @@ const commonProps = {
filteringProperties,
filteringOptions: [],
i18nStrings,
i18nStringsTokenGroups,
countText: '5 matches',
disableFreeTextFiltering: false,
virtualScroll: true,
Expand Down
28 changes: 3 additions & 25 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12063,18 +12063,7 @@ Object {
"description": "Fired when the \`query\` gets changed. Filter the dataset in response to this event using the values in the \`detail\` object.",
"detailInlineType": Object {
"name": "PropertyFilterProps.Query",
"properties": Array [
Object {
"name": "operation",
"optional": false,
"type": "PropertyFilterProps.JoinOperation",
},
Object {
"name": "tokens",
"optional": false,
"type": "ReadonlyArray<PropertyFilterProps.Token>",
},
],
"properties": Array [],
"type": "object",
},
"detailType": "PropertyFilterProps.Query",
Expand Down Expand Up @@ -12160,7 +12149,7 @@ To use it correctly, define an ID for the element you want to use as label and s
"type": "string",
},
Object {
"description": "Set \`asyncProperties\` if you need to load \`filteringProperties\` asynchronousely. This would cause extra \`onLoadMore\`
"description": "Set \`asyncProperties\` if you need to load \`filteringProperties\` asynchronously. This would cause extra \`onLoadMore\`
events to fire calling for more properties.",
"name": "asyncProperties",
"optional": true,
Expand Down Expand Up @@ -12552,18 +12541,7 @@ Each token has the following properties:
",
"inlineType": Object {
"name": "PropertyFilterProps.Query",
"properties": Array [
Object {
"name": "operation",
"optional": false,
"type": "PropertyFilterProps.JoinOperation",
},
Object {
"name": "tokens",
"optional": false,
"type": "ReadonlyArray<PropertyFilterProps.Token>",
},
],
"properties": Array [],
"type": "object",
},
"name": "query",
Expand Down
1 change: 1 addition & 0 deletions src/autosuggest/options-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const useAutosuggestItems = ({
const enteredItemLabel = i18n('enteredTextLabel', enteredTextLabel?.(filterValue), format =>
format({ value: filterValue })
);

if (!enteredItemLabel) {
warnOnce('Autosuggest', 'A value for enteredTextLabel must be provided.');
}
Expand Down
77 changes: 72 additions & 5 deletions src/property-filter/__tests__/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
import React, { useState } from 'react';

import PropertyFilter from '../../../lib/components/property-filter';
import { FilteringProperty, InternalFilteringProperty, PropertyFilterProps, Token } from '../interfaces';
import PropertyFilterInternal, { PropertyFilterInternalProps } from '../../../lib/components/property-filter/internal';
import {
FilteringProperty,
I18nStrings,
I18nStringsTokenGroups,
InternalFilteringProperty,
PropertyFilterProps,
Token,
} from '../interfaces';

export const i18nStrings = {
export const i18nStrings: I18nStrings = {
dismissAriaLabel: 'Dismiss',

groupValuesText: 'Values',
Expand Down Expand Up @@ -37,10 +45,64 @@ export const i18nStrings = {
tokenLimitShowFewer: 'Show fewer',
clearFiltersText: 'Clear filters',
tokenOperatorAriaLabel: 'Boolean Operator',
removeTokenButtonAriaLabel: (token: Token) =>
'Remove token ' + token.propertyKey + ' ' + token.operator + ' ' + token.value,
enteredTextLabel: (text: string) => `Use: "${text}"`,
} as const;

formatToken: token => `${token.propertyLabel} ${formatOperator(token.operator)} ${token.value}`,
removeTokenButtonAriaLabel: (token: Token) =>
'Remove token ' + token.propertyKey + ' ' + formatOperator(token.operator) + ' ' + token.value,
};

export const i18nStringsTokenGroups: I18nStringsTokenGroups = {
groupEditAriaLabel: group =>
'Edit filter, ' +
group.tokens
.map(token => `${token.propertyLabel} ${formatOperator(token.operator)} ${token.value}`)
.join(` ${group.operationLabel} `),
tokenEditorTokenActionsAriaLabel: token =>
`Remove actions, ${token.propertyLabel} ${formatOperator(token.operator)} ${token.value}`,
tokenEditorTokenRemoveAriaLabel: token =>
`Remove filter, ${token.propertyLabel} ${formatOperator(token.operator)} ${token.value}`,
tokenEditorTokenRemoveLabel: 'Remove filter',
tokenEditorTokenRemoveFromGroupLabel: 'Remove filter from group',
tokenEditorAddNewTokenLabel: 'Add new filter',
tokenEditorAddTokenActionsAriaLabel: 'Add filter actions',
tokenEditorAddExistingTokenAriaLabel: token =>
`Add filter ${token.propertyLabel} ${formatOperator(token.operator)} ${token.value} to group`,
tokenEditorAddExistingTokenLabel: token =>
`Add filter ${token.propertyLabel} ${token.operator} ${token.value} to group`,
};

export const providedI18nStrings = {
'property-filter': {
'i18nStrings.editTokenHeader': 'Edit token',
'i18nStrings.propertyText': 'Property',
'i18nStrings.operatorText': 'Operator',
'i18nStrings.valueText': 'Value',
'i18nStrings.cancelActionText': 'Cancel',
'i18nStrings.applyActionText': 'Apply',
'i18nStrings.formatToken': '{token__propertyLabel} {token__operator} {token__value}',
'i18nStrings.tokenEditorTokenActionsAriaLabel': 'Remove actions, {token__formattedText}',
'i18nStrings.tokenEditorTokenRemoveAriaLabel': 'Remove filter, {token__formattedText}',
'i18nStrings.tokenEditorTokenRemoveLabel': 'Remove filter',
'i18nStrings.tokenEditorTokenRemoveFromGroupLabel': 'Remove filter from group',
'i18nStrings.tokenEditorAddNewTokenLabel': 'Add new filter',
'i18nStrings.tokenEditorAddTokenActionsAriaLabel': 'Add filter actions',
'i18nStrings.tokenEditorAddExistingTokenAriaLabel': 'Add filter {token__formattedText} to group',
'i18nStrings.tokenEditorAddExistingTokenLabel':
'Add filter {token__propertyLabel} {token__operator} {token__value} to group',
},
};

function formatOperator(operator: string) {
switch (operator) {
case '=':
return 'equals';
case '!=':
return 'does_not_equal';
default:
return operator;
}
}

export const createDefaultProps = (
filteringProperties: PropertyFilterProps['filteringProperties'],
Expand Down Expand Up @@ -75,3 +137,8 @@ export function StatefulPropertyFilter(props: Omit<PropertyFilterProps, 'onChang
const [query, setQuery] = useState<PropertyFilterProps.Query>(props.query);
return <PropertyFilter {...props} query={query} onChange={e => setQuery(e.detail)} />;
}

export function StatefulInternalPropertyFilter(props: Omit<PropertyFilterInternalProps, 'onChange'>) {
const [query, setQuery] = useState<PropertyFilterInternalProps['query']>(props.query);
return <PropertyFilterInternal {...props} query={query} onChange={e => setQuery(e.detail)} />;
}
72 changes: 72 additions & 0 deletions src/property-filter/__tests__/property-filter-i18n.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { act, render } from '@testing-library/react';

import TestI18nProvider from '../../../lib/components/i18n/testing';
import PropertyFilter from '../../../lib/components/property-filter';
import InternalPropertyFilter from '../../../lib/components/property-filter/internal';
import createWrapper, { ElementWrapper, PropertyFilterWrapper } from '../../../lib/components/test-utils/dom';
import { PropertyFilterWrapperInternal } from '../../../lib/components/test-utils/dom/property-filter/index.js';
import { createDefaultProps } from './common';

import styles from '../../../lib/components/property-filter/styles.selectors.js';
Expand Down Expand Up @@ -251,6 +253,76 @@ describe('i18n', () => {
expect(findSubmitButton(popoverContent).getElement()).toHaveTextContent('Custom Apply');
});

it('uses token group edit label from i18n provider', () => {
const { container } = render(
<TestI18nProvider
messages={{
'property-filter': {
'i18nStrings.operationAndText': '&',
'i18nStrings.operationOrText': '|',
'i18nStrings.formatToken': `{token__operator, select,
equals {{token__propertyLabel} eq {token__value}}
not_equals {{token__propertyLabel} neq {token__value}}
other {}}`,
'i18nStrings.groupEditAriaLabel': `{group__formattedTokens__length, select,
2 {Edit filter group {group__formattedTokens0__formattedText} {group__operationLabel} {group__formattedTokens1__formattedText}}
3 {Edit filter group {group__formattedTokens0__formattedText} {group__operationLabel} {group__formattedTokens1__formattedText} {group__operationLabel} {group__formattedTokens2__formattedText}}
4 {Edit filter group {group__formattedTokens0__formattedText} {group__operationLabel} {group__formattedTokens1__formattedText} {group__operationLabel} {group__formattedTokens2__formattedText} {group__operationLabel} {group__formattedTokens3__formattedText}}
5 {Edit filter group {group__formattedTokens0__formattedText} {group__operationLabel} {group__formattedTokens1__formattedText} {group__operationLabel} {group__formattedTokens2__formattedText} {group__operationLabel} {group__formattedTokens3__formattedText} {group__operationLabel} 1 more}
other {Edit filter group {group__formattedTokens0__formattedText} {group__operationLabel} {group__formattedTokens1__formattedText} {group__operationLabel} {group__formattedTokens2__formattedText} {group__operationLabel} {group__formattedTokens3__formattedText} {group__operationLabel} more}}`,
'i18nStrings.removeTokenButtonAriaLabel': `Remove filter, {token__formattedText}`,
},
}}
>
<InternalPropertyFilter
{...defaultProps}
i18nStrings={{}}
query={{
operation: 'and',
tokenGroups: [
{
operation: 'or',
tokens: [
{ propertyKey: 'string', operator: '=', value: 'value1' },
{ propertyKey: 'string', operator: '=', value: 'value2' },
{ propertyKey: 'string', operator: '=', value: 'value3' },
{ propertyKey: 'string', operator: '=', value: 'value4' },
{ propertyKey: 'string', operator: '=', value: 'value5' },
],
},
],
tokens: [],
}}
enableTokenGroups={true}
filteringOptions={[]}
customGroupsText={[]}
disableFreeTextFiltering={false}
/>
</TestI18nProvider>
);
const wrapper = new PropertyFilterWrapperInternal(createWrapper(container).findPropertyFilter()!.getElement());
const token = (index: number) => wrapper.findTokens()[index];
const groupToken = (index: number, inGroupIndex: number) => token(index).findGroupTokens()[inGroupIndex];

// 1st nested token
expect(groupToken(0, 0).getElement()).toHaveAccessibleName('String eq value1');
expect(groupToken(0, 0).findTokenOperation()).toBe(null);
expect(groupToken(0, 0).findRemoveButton().getElement()).toHaveAccessibleName('Remove filter, String eq value1');

// 1nd nested token
expect(groupToken(0, 1).getElement()).toHaveAccessibleName('String eq value2');
expect(groupToken(0, 1).findTokenOperation()!.getElement()).toHaveTextContent('|');
expect(groupToken(0, 1).findRemoveButton().getElement()).toHaveAccessibleName('Remove filter, String eq value2');

// Token group
expect(token(0).getElement()).toHaveAccessibleName(
'String eq value1 | String eq value2 | String eq value3 | String eq value4 | String eq value5'
);
expect(token(0).findEditButton()!.getElement()).toHaveAccessibleName(
'Edit filter group String eq value1 | String eq value2 | String eq value3 | String eq value4 | 1 more'
);
});

it('uses formatted token for removeTokenButtonAriaLabel', () => {
const { container } = render(
<PropertyFilter
Expand Down
Loading