Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens]Adds selected field accordion to the fields list #143175

Merged
merged 2 commits into from
Oct 12, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,31 @@ describe('FormBased Data Panel', () => {
}),
};
});

it('should list all selected fields if exist', async () => {
const newProps = {
...props,
layerFields: ['bytes'],
};
const wrapper = mountWithIntl(<InnerFormBasedDataPanel {...newProps} />);
expect(
wrapper
.find('[data-test-subj="lnsIndexPatternSelectedFields"]')
.find(FieldItem)
.map((fieldItem) => fieldItem.prop('field').name)
).toEqual(['bytes']);
});

it('should not list the selected fields accordion if no fields given', async () => {
const wrapper = mountWithIntl(<InnerFormBasedDataPanel {...props} />);
expect(
wrapper
.find('[data-test-subj="lnsIndexPatternSelectedFields"]')
.find(FieldItem)
.map((fieldItem) => fieldItem.prop('field').name)
).toEqual([]);
});

it('should list all supported fields in the pattern sorted alphabetically in groups', async () => {
const wrapper = mountWithIntl(<InnerFormBasedDataPanel {...props} />);
expect(wrapper.find(FieldItem).first().prop('field').displayName).toEqual('Records');
Expand Down
20 changes: 20 additions & 0 deletions x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type Props = Omit<
frame: FramePublicAPI;
indexPatternService: IndexPatternServiceAPI;
onIndexPatternRefresh: () => void;
layerFields?: string[];
};

function sortFields(fieldA: IndexPatternField, fieldB: IndexPatternField) {
Expand Down Expand Up @@ -144,6 +145,7 @@ export function FormBasedDataPanel({
frame,
onIndexPatternRefresh,
usedIndexPatterns,
layerFields,
}: Props) {
const { indexPatterns, indexPatternRefs, existingFields, isFirstExistenceFetch } =
frame.dataViews;
Expand Down Expand Up @@ -234,6 +236,7 @@ export function FormBasedDataPanel({
indexPatternService={indexPatternService}
onIndexPatternRefresh={onIndexPatternRefresh}
frame={frame}
layerFields={layerFields}
/>
)}
</>
Expand Down Expand Up @@ -282,6 +285,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({
indexPatternService,
frame,
onIndexPatternRefresh,
layerFields,
}: Omit<
DatasourceDataPanelProps,
'state' | 'setState' | 'showNoDataPopover' | 'core' | 'onChangeIndexPattern' | 'usedIndexPatterns'
Expand All @@ -296,6 +300,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({
frame: FramePublicAPI;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
onIndexPatternRefresh: () => void;
layerFields?: string[];
}) {
const [localState, setLocalState] = useState<DataPanelState>({
nameFilter: '',
Expand Down Expand Up @@ -345,6 +350,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({
const allSupportedTypesFields = allFields.filter((field) =>
supportedFieldTypes.has(field.type)
);
const usedByLayersFields = allFields.filter((field) => layerFields?.includes(field.name));
const sorted = allSupportedTypesFields.sort(sortFields);
const groupedFields = {
...defaultFieldGroups,
Expand Down Expand Up @@ -372,6 +378,19 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({
title: '',
hideDetails: true,
},
SelectedFields: {
fields: usedByLayersFields,
fieldCount: usedByLayersFields.length,
isInitiallyOpen: true,
showInAccordion: true,
title: i18n.translate('xpack.lens.indexPattern.selectedFieldsLabel', {
defaultMessage: 'Selected fields',
}),
isAffectedByGlobalFilter: !!filters.length,
isAffectedByTimeFilter: true,
hideDetails: false,
hideIfEmpty: true,
},
AvailableFields: {
fields: groupedFields.availableFields,
fieldCount: groupedFields.availableFields.length,
Expand Down Expand Up @@ -451,6 +470,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({
existenceFetchTimeout,
currentIndexPattern,
existingFieldsForIndexPattern,
layerFields,
]);

const fieldGroups: FieldGroups = useMemo(() => {
Expand Down
102 changes: 53 additions & 49 deletions x-pack/plugins/lens/public/datasources/form_based/field_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type FieldGroups = Record<
isAffectedByTimeFilter: boolean;
hideDetails?: boolean;
defaultNoFieldsMessage?: string;
hideIfEmpty?: boolean;
}
>;

Expand Down Expand Up @@ -161,55 +162,58 @@ export const FieldList = React.memo(function FieldList({
)}
</ul>
<EuiSpacer size="s" />
{fieldGroupsToShow.map(([key, fieldGroup], index) => (
<Fragment key={key}>
<FieldsAccordion
dropOntoWorkspace={dropOntoWorkspace}
hasSuggestionForField={hasSuggestionForField}
initialIsOpen={Boolean(accordionState[key])}
key={key}
id={`lnsIndexPattern${key}`}
label={fieldGroup.title}
helpTooltip={fieldGroup.helpText}
exists={exists}
editField={editField}
removeField={removeField}
hideDetails={fieldGroup.hideDetails}
hasLoaded={!!hasSyncedExistingFields}
fieldsCount={fieldGroup.fields.length}
isFiltered={fieldGroup.fieldCount !== fieldGroup.fields.length}
paginatedFields={paginatedFields[key]}
fieldProps={fieldProps}
groupIndex={index + 1}
onToggle={(open) => {
setAccordionState((s) => ({
...s,
[key]: open,
}));
const displayedFieldLength = getDisplayedFieldsLength(fieldGroups, {
...accordionState,
[key]: open,
});
setPageSize(
Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength))
);
}}
showExistenceFetchError={existenceFetchFailed}
showExistenceFetchTimeout={existenceFetchTimeout}
renderCallout={
<NoFieldsCallout
isAffectedByGlobalFilter={fieldGroup.isAffectedByGlobalFilter}
isAffectedByTimerange={fieldGroup.isAffectedByTimeFilter}
isAffectedByFieldFilter={fieldGroup.fieldCount !== fieldGroup.fields.length}
existFieldsInIndex={!!existFieldsInIndex}
defaultNoFieldsMessage={fieldGroup.defaultNoFieldsMessage}
/>
}
uiActions={uiActions}
/>
<EuiSpacer size="m" />
</Fragment>
))}
{fieldGroupsToShow.map(([key, fieldGroup], index) => {
if (Boolean(fieldGroup.hideIfEmpty) && !fieldGroup.fields.length) return null;
return (
<Fragment key={key}>
<FieldsAccordion
dropOntoWorkspace={dropOntoWorkspace}
hasSuggestionForField={hasSuggestionForField}
initialIsOpen={Boolean(accordionState[key])}
key={key}
id={`lnsIndexPattern${key}`}
label={fieldGroup.title}
helpTooltip={fieldGroup.helpText}
exists={exists}
editField={editField}
removeField={removeField}
hideDetails={fieldGroup.hideDetails}
hasLoaded={!!hasSyncedExistingFields}
fieldsCount={fieldGroup.fields.length}
isFiltered={fieldGroup.fieldCount !== fieldGroup.fields.length}
paginatedFields={paginatedFields[key]}
fieldProps={fieldProps}
groupIndex={index + 1}
onToggle={(open) => {
setAccordionState((s) => ({
...s,
[key]: open,
}));
const displayedFieldLength = getDisplayedFieldsLength(fieldGroups, {
...accordionState,
[key]: open,
});
setPageSize(
Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength))
);
}}
showExistenceFetchError={existenceFetchFailed}
showExistenceFetchTimeout={existenceFetchTimeout}
renderCallout={
<NoFieldsCallout
isAffectedByGlobalFilter={fieldGroup.isAffectedByGlobalFilter}
isAffectedByTimerange={fieldGroup.isAffectedByTimeFilter}
isAffectedByFieldFilter={fieldGroup.fieldCount !== fieldGroup.fields.length}
existFieldsInIndex={!!existFieldsInIndex}
defaultNoFieldsMessage={fieldGroup.defaultNoFieldsMessage}
/>
}
uiActions={uiActions}
/>
<EuiSpacer size="m" />
</Fragment>
);
})}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,20 @@ describe('IndexPattern Data Source', () => {
});
});

describe('#getSelectedFields', () => {
it('should return the fields used per layer', async () => {
expect(FormBasedDatasource?.getSelectedFields?.(baseState)).toEqual(['op']);
});

it('should return empty array for empty layers', async () => {
const state = {
...baseState,
layers: {},
};
expect(FormBasedDatasource?.getSelectedFields?.(state)).toEqual([]);
});
});

describe('#toExpression', () => {
it('should generate an empty expression when no columns are selected', async () => {
const state = FormBasedDatasource.initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,26 @@ export function getFormBasedDatasource({
});
},

getSelectedFields(state) {
const fields: string[] = [];
Object.values(state?.layers)?.forEach((l) => {
const { columns } = l;
Object.values(columns).forEach((c) => {
if ('sourceField' in c) {
fields.push(c.sourceField);
}
});
});
return fields;
},

toExpression: (state, layerId, indexPatterns) =>
toExpression(state, layerId, indexPatterns, uiSettings),

renderDataPanel(domElement: Element, props: DatasourceDataPanelProps<FormBasedPrivateState>) {
const { onChangeIndexPattern, ...otherProps } = props;
const layerFields = formBasedDatasource?.getSelectedFields?.(props.state);

render(
<KibanaThemeProvider theme$={core.theme.theme$}>
<I18nProvider>
Expand All @@ -292,6 +307,7 @@ export function getFormBasedDatasource({
core={core}
uiActions={uiActions}
onIndexPatternRefresh={onRefreshIndexPattern}
layerFields={layerFields}
/>
</KibanaContextProvider>
</I18nProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ describe('TextBased Query Languages Data Panel', () => {
).toEqual(['timestamp', 'bytes', 'memory']);
});

it('should not display the selected fields accordion if there are no fields displayed', async () => {
const wrapper = mountWithIntl(<TextBasedDataPanel {...defaultProps} />);
expect(wrapper.find('[data-test-subj="lnsSelectedFieldsTextBased"]').length).toEqual(0);
});

it('should display the selected fields accordion if there are fields displayed', async () => {
const props = {
...defaultProps,
layerFields: ['memory'],
};
const wrapper = mountWithIntl(<TextBasedDataPanel {...props} />);
expect(wrapper.find('[data-test-subj="lnsSelectedFieldsTextBased"]').length).not.toEqual(0);
});

it('should list all supported fields in the pattern that match the search input', async () => {
const wrapper = mountWithIntl(<TextBasedDataPanel {...defaultProps} />);
const searchBox = wrapper.find('[data-test-subj="lnsTextBasedLangugesFieldSearch"]');
Expand Down
Loading