diff --git a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration.tsx b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration.tsx index 9e06cbb6e50..4b07bce9569 100644 --- a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration.tsx @@ -1,118 +1,134 @@ -import ChartBuilderSaveActions from '@admin/pages/release/datablocks/components/chart/ChartBuilderSaveActions'; -import { useChartBuilderFormsContext } from '@admin/pages/release/datablocks/components/chart/contexts/ChartBuilderFormsContext'; +import ChartBoundaryLevelsForm, { + ChartBoundaryLevelsFormValues, +} from '@admin/pages/release/datablocks/components/chart/ChartBoundaryLevelsForm'; import { ChartOptions } from '@admin/pages/release/datablocks/components/chart/reducers/chartBuilderReducer'; -import Effect from '@common/components/Effect'; -import FormProvider from '@common/components/form/FormProvider'; -import Form from '@common/components/form/Form'; -import FormFieldSelect from '@common/components/form/FormFieldSelect'; +import { + AxisConfiguration, + MapConfig, +} from '@common/modules/charts/types/chart'; +import { LegendConfiguration } from '@common/modules/charts/types/legend'; +import createDataSetCategories from '@common/modules/charts/util/createDataSetCategories'; +import expandDataSet from '@common/modules/charts/util/expandDataSet'; +import generateDataSetKey from '@common/modules/charts/util/generateDataSetKey'; +import getDataSetCategoryConfigs from '@common/modules/charts/util/getDataSetCategoryConfigs'; import { FullTableMeta } from '@common/modules/table-tool/types/fullTable'; +import { TableDataResult } from '@common/services/tableBuilderService'; import parseNumber from '@common/utils/number/parseNumber'; -import Yup from '@common/validation/yup'; -import merge from 'lodash/merge'; -import React, { ReactNode, useCallback } from 'react'; - -const formId = 'chartBoundaryLevelsConfigurationForm'; - -interface FormValues { - boundaryLevel?: number; -} +import { isEqual } from 'lodash'; +import React, { ReactNode, useCallback, useMemo } from 'react'; +import generateDataSetLabel from './utils/generateDataSetLabel'; interface Props { buttons?: ReactNode; + axisMajor: AxisConfiguration; + data: TableDataResult[]; + legend: LegendConfiguration; + map?: MapConfig; meta: FullTableMeta; options: ChartOptions; - onChange: (values: ChartOptions) => void; - onSubmit: (chartOptions: ChartOptions) => void; + onChange: (values: Partial) => void; + onSubmit: (values: ChartBoundaryLevelsFormValues) => void; } export default function ChartBoundaryLevelsConfiguration({ buttons, + axisMajor, + data, + legend, + map, meta, options, onChange, onSubmit, }: Props) { - const { updateForm, submitForms } = useChartBuilderFormsContext(); - const normalizeValues = useCallback( - (values: FormValues): ChartOptions => { + ( + values: Partial, + ): ChartBoundaryLevelsFormValues => { // Use `merge` as we want to avoid potential undefined // values from overwriting existing values - return merge({}, options, values, { + return { boundaryLevel: values.boundaryLevel ? parseNumber(values.boundaryLevel) : undefined, - }); + dataSetConfigs: + values.dataSetConfigs?.map(({ boundaryLevel, dataSet }) => ({ + boundaryLevel: parseNumber(boundaryLevel), + dataSet, + })) ?? [], + }; }, - [options], + [], ); + const handleSubmit = useCallback( + (values: ChartBoundaryLevelsFormValues) => { + onSubmit(normalizeValues(values)); + }, + [onSubmit, normalizeValues], + ); const handleChange = useCallback( - (values: FormValues) => { + (values: Partial) => { onChange(normalizeValues(values)); }, - [normalizeValues, onChange], + [onChange, normalizeValues], ); - return ( - ({ - boundaryLevel: Yup.number() - .transform(value => (Number.isNaN(value) ? undefined : value)) - .nullable() - .oneOf(meta.boundaryLevels.map(level => level.id)) - .required('Choose a boundary level'), - })} - > - {({ formState, watch }) => { - const values = watch(); - return ( -
{ - onSubmit(normalizeValues(values)); - await submitForms(); - }} - > - - - - label="Boundary level" - hint="Select a version of geographical data to use" - name="boundaryLevel" - order={[]} - options={[ - { - label: 'Please select', - value: '', - }, - ...meta.boundaryLevels.map(({ id, label }) => ({ - value: id, - label, - })), - ]} - /> + const { dataSetConfigs, boundaryLevel } = + useMemo(() => { + const dataSetCategories = createDataSetCategories({ + axisConfiguration: { + ...axisMajor, + groupBy: 'locations', + }, + data, + meta, + }); + + const dataSetCategoryConfigs = getDataSetCategoryConfigs({ + dataSetCategories, + legendItems: legend.items, + meta, + deprecatedDataClassification: options.dataClassification, + deprecatedDataGroups: options.dataGroups, + }); + + return { + boundaryLevel: options.boundaryLevel, + dataSetConfigs: dataSetCategoryConfigs.map(({ rawDataSet }) => ({ + dataSet: rawDataSet, + boundaryLevel: map?.dataSetConfigs.find(config => + isEqual(config.dataSet, rawDataSet), + )?.boundaryLevel, + })), + }; + }, [axisMajor, data, meta, legend.items, map, options]); - - {buttons} - - - ); - }} -
+ const mappedDataSetConfigs = useMemo(() => { + return Object.values(dataSetConfigs).map(dataSetConfig => { + const expandedDataSet = expandDataSet(dataSetConfig.dataSet, meta); + const label = generateDataSetLabel(expandedDataSet); + const key = generateDataSetKey(dataSetConfig.dataSet); + + return { + label, + key, + }; + }); + }, [meta, dataSetConfigs]); + + return ( + ({ + label, + value: id, + }))} + dataSetRows={mappedDataSetConfigs} + initialValues={{ boundaryLevel, dataSetConfigs }} + onChange={handleChange} + onSubmit={handleSubmit} + /> ); } diff --git a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsForm.tsx b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsForm.tsx new file mode 100644 index 00000000000..b28e104d93a --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBoundaryLevelsForm.tsx @@ -0,0 +1,163 @@ +import ChartBuilderSaveActions from '@admin/pages/release/datablocks/components/chart/ChartBuilderSaveActions'; +import { useChartBuilderFormsContext } from '@admin/pages/release/datablocks/components/chart/contexts/ChartBuilderFormsContext'; +import Effect from '@common/components/Effect'; +import Form from '@common/components/form/Form'; +import FormFieldSelect from '@common/components/form/FormFieldSelect'; +import FormProvider from '@common/components/form/FormProvider'; +import { MapDataSetConfig } from '@common/modules/charts/types/chart'; +import Yup from '@common/validation/yup'; +import React, { ReactNode, useMemo } from 'react'; +import { AnyObject, NumberSchema, ObjectSchema } from 'yup'; + +const formId = 'chartBoundaryLevelsConfigurationForm'; + +export interface ChartBoundaryLevelsFormValues { + boundaryLevel?: number; + dataSetConfigs: Omit[]; +} + +interface Props { + hasDataSetBoundaryLevels?: boolean; // feature flag (remove once) + buttons?: ReactNode; + boundaryLevelOptions: { label: string; value: number }[]; + initialValues?: ChartBoundaryLevelsFormValues; + dataSetRows: { key: string; label: string }[]; + onChange: (values: Partial) => void; + onSubmit: (values: ChartBoundaryLevelsFormValues) => void; +} + +export default function ChartBoundaryLevelsForm({ + hasDataSetBoundaryLevels = true, + buttons, + boundaryLevelOptions, + dataSetRows, + initialValues, + onChange, + onSubmit, +}: Props) { + const { updateForm, submitForms } = useChartBuilderFormsContext(); + + const validationSchema = useMemo< + ObjectSchema + >(() => { + return Yup.object({ + boundaryLevel: Yup.number() + .transform(value => (Number.isNaN(value) ? undefined : value)) + .nullable() + .oneOf(boundaryLevelOptions.map(({ value }) => value)) + .required('Choose a boundary level'), + dataSetConfigs: Yup.array() + .of( + Yup.object({ + boundaryLevel: Yup.mixed().test( + 'dataset-boundary-is-number-or-empty', + 'Must be a number or an empty string', + value => !Number.isNaN(value) || value === '', + ) as NumberSchema, + dataSet: Yup.object({ + filters: Yup.array().required(), + }).required(), + }), + ) + .required(), + }); + }, [boundaryLevelOptions]); + + return ( + + {({ formState, watch }) => { + const values = watch(); + return ( + + id={formId} + onSubmit={onSubmit} + onChange={onChange} + > + + + label={ + hasDataSetBoundaryLevels + ? 'Default boundary level' + : 'Boundary level' + } + hint={`Select a version of geographical data to use${ + hasDataSetBoundaryLevels + ? "across any data sets that don't have a specific one set" + : '' + }`} + name="boundaryLevel" + order={[]} + options={[ + { + label: 'Please select', + value: '', + }, + ...boundaryLevelOptions, + ]} + /> + {hasDataSetBoundaryLevels && dataSetRows.length > 1 && ( + <> +

Set boundary levels per data set

+ + + + + + + + + {dataSetRows.map(({ key, label }, index) => { + return ( + + + + + ); + })} + +
Data setBoundary
{label} + +
+ + )} + + { + onSubmit(values); + await submitForms(); + }} + > + {buttons} + + + ); + }} +
+ ); +} diff --git a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBuilder.tsx b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBuilder.tsx index e2e26b008b4..4f88b6e4a50 100644 --- a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBuilder.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/ChartBuilder.tsx @@ -1,17 +1,14 @@ import useGetChartFile from '@admin/hooks/useGetChartFile'; import ChartAxisConfiguration from '@admin/pages/release/datablocks/components/chart/ChartAxisConfiguration'; +import ChartBoundaryLevelsConfiguration from '@admin/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration'; import ChartBuilderPreview from '@admin/pages/release/datablocks/components/chart/ChartBuilderPreview'; import ChartConfiguration from '@admin/pages/release/datablocks/components/chart/ChartConfiguration'; +import ChartDataGroupingsConfiguration from '@admin/pages/release/datablocks/components/chart/ChartDataGroupingsConfiguration'; import ChartDataSetsConfiguration from '@admin/pages/release/datablocks/components/chart/ChartDataSetsConfiguration'; import ChartDefinitionSelector from '@admin/pages/release/datablocks/components/chart/ChartDefinitionSelector'; import ChartLegendConfiguration from '@admin/pages/release/datablocks/components/chart/ChartLegendConfiguration'; -import ChartBoundaryLevelsConfiguration from '@admin/pages/release/datablocks/components/chart/ChartBoundaryLevelsConfiguration'; -import ChartDataGroupingsConfiguration from '@admin/pages/release/datablocks/components/chart/ChartDataGroupingsConfiguration'; import { ChartBuilderFormsContextProvider } from '@admin/pages/release/datablocks/components/chart/contexts/ChartBuilderFormsContext'; -import { - ChartOptions, - useChartBuilderReducer, -} from '@admin/pages/release/datablocks/components/chart/reducers/chartBuilderReducer'; +import { useChartBuilderReducer } from '@admin/pages/release/datablocks/components/chart/reducers/chartBuilderReducer'; import Button from '@common/components/Button'; import ModalConfirm from '@common/components/ModalConfirm'; import Tabs from '@common/components/Tabs'; @@ -54,6 +51,7 @@ import { isServerValidationError } from '@common/validation/serverValidations'; import { produce } from 'immer'; import omit from 'lodash/omit'; import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { ChartBoundaryLevelsFormValues } from './ChartBoundaryLevelsForm'; const chartDefinitions: ChartDefinition[] = [ lineChartBlockDefinition, @@ -274,14 +272,17 @@ const ChartBuilder = ({ 200, ); - const handleBoundaryLevelChange = useCallback( - async (values: ChartOptions) => { - actions.updateChartOptions(values); + const handleDefaultBoundaryLevelChange = useCallback( + async ({ + boundaryLevel, + dataSetConfigs, + }: ChartBoundaryLevelsFormValues) => { + actions.updateChartBoundaryLevels({ boundaryLevel, dataSetConfigs }); setDataLoading(true); await onTableQueryUpdate({ - boundaryLevel: parseNumber(values.boundaryLevel), + boundaryLevel: parseNumber(boundaryLevel), }); setDataLoading(false); @@ -357,9 +358,11 @@ const ChartBuilder = ({ )} {forms.boundaryLevels && + axes.major && + forms.dataGroupings && definition?.type === 'map' && options && - meta.boundaryLevels.length && ( + legend && ( )} @@ -385,13 +392,13 @@ const ChartBuilder = ({ id={forms.dataGroupings.id} > { actions.updateChartMapConfiguration(values); @@ -406,7 +413,6 @@ const ChartBuilder = ({ /> )} - {forms.legend && axes.major && legend && ( {editDataSetConfig && ( - + setEditDataSetConfig(undefined)} + > { + const testTable = testFullTable; + const testDefaultChartOptions: ChartOptions = { alt: '', height: 600, @@ -35,7 +39,6 @@ describe('ChartBoundaryLevelsConfiguration', () => { }, ], }; - const testFormState: ChartBuilderForms = { options: { isValid: true, @@ -50,6 +53,18 @@ describe('ChartBoundaryLevelsConfiguration', () => { title: 'Boundary levels configuration', }, }; + const testDataSets: DataSet[] = [ + { + filters: ['ethnicity-major-chinese', 'state-funded-primary'], + indicator: 'authorised-absence-sessions', + timePeriod: '2014_AY', + }, + { + filters: ['ethnicity-major-chinese', 'state-funded-primary'], + indicator: 'authorised-absence-sessions', + timePeriod: '2015_AY', + }, + ]; function render(element: ReactElement) { return baseRender( @@ -63,19 +78,57 @@ describe('ChartBoundaryLevelsConfiguration', () => { ); } + test('renders without table one or less dataset is included', () => { + render( + , + ); + expect( + screen.queryByText('Set boundary levels per data set'), + ).not.toBeInTheDocument(); + }); + test('renders correctly without initial values', () => { render( , ); - expect(screen.getByLabelText('Boundary level')).not.toHaveValue(); + expect(screen.getByLabelText('Default boundary level')).not.toHaveValue(); const boundaryLevels = within( - screen.getByLabelText('Boundary level'), + screen.getByLabelText('Default boundary level'), ).getAllByRole('option'); expect(boundaryLevels).toHaveLength(4); @@ -87,22 +140,59 @@ describe('ChartBoundaryLevelsConfiguration', () => { expect(boundaryLevels[2]).toHaveValue('2'); expect(boundaryLevels[3]).toHaveTextContent('Boundary level 3'); expect(boundaryLevels[3]).toHaveValue('3'); + + expect( + screen.getByText('Set boundary levels per data set'), + ).toBeInTheDocument(); + + const rows = screen.getAllByRole('row'); + expect(rows).toHaveLength(3); + + const row1Cells = within(rows[1]).getAllByRole('cell'); + expect(row1Cells[0]).toHaveTextContent( + 'Number of authorised absence sessions (Ethnicity Major Chinese, State-funded primary, All locations, 2014/15)', + ); + expect(row1Cells[1]).toHaveTextContent('Use default'); }); test('renders correctly with initial values', () => { render( ({ + dataSet, + dataGrouping: { type: 'Custom', customGroups: [] }, + boundaryLevel: 3, + })), + }} onChange={noop} onSubmit={noop} />, ); - expect(screen.getByLabelText('Boundary level')).toHaveValue('2'); + expect(screen.getByLabelText('Default boundary level')).toHaveValue('2'); + const rows = screen.getAllByRole('row'); + expect(rows).toHaveLength(3); + + expect(within(rows[1]).getByRole('combobox')).toHaveValue('3'); + expect(within(rows[2]).getByRole('combobox')).toHaveValue('3'); }); test('calls `onChange` handler when form values change', async () => { @@ -110,25 +200,62 @@ describe('ChartBoundaryLevelsConfiguration', () => { const { user } = render( , ); - await user.selectOptions(screen.getByLabelText('Boundary level'), ['2']); + await user.selectOptions(screen.getByLabelText('Default boundary level'), [ + '2', + ]); - expect(handleChange).toHaveBeenCalledWith<[ChartOptions]>({ - ...testDefaultChartOptions, + expect(handleChange).toHaveBeenCalledWith<[ChartBoundaryLevelsFormValues]>({ boundaryLevel: 2, + dataSetConfigs: testDataSets.map(dataSet => ({ dataSet })), + }); + + const rows = screen.getAllByRole('row'); + await user.selectOptions(within(rows[1]).getByRole('combobox'), ['2']); + + expect(handleChange).toHaveBeenCalledWith<[ChartBoundaryLevelsFormValues]>({ + boundaryLevel: 2, + dataSetConfigs: [ + { dataSet: testDataSets[0], boundaryLevel: 2 }, + { dataSet: testDataSets[1] }, + ], }); }); test('submitting fails with validation errors if no boundary level set', async () => { const { user } = render( { const { user } = render( , ); - await user.selectOptions(screen.getByLabelText('Boundary level'), ['2']); + await user.selectOptions(screen.getByLabelText('Default boundary level'), [ + '2', + ]); expect(handleSubmit).not.toHaveBeenCalled(); @@ -172,9 +313,11 @@ describe('ChartBoundaryLevelsConfiguration', () => { ); await waitFor(() => { - expect(handleSubmit).toHaveBeenCalledWith<[ChartOptions]>({ - ...testDefaultChartOptions, + expect(handleSubmit).toHaveBeenCalledWith< + [ChartBoundaryLevelsFormValues] + >({ boundaryLevel: 2, + dataSetConfigs: testDataSets.map(dataSet => ({ dataSet })), }); }); }); @@ -184,7 +327,19 @@ describe('ChartBoundaryLevelsConfiguration', () => { const { user } = render( { ); await waitFor(() => { - expect(handleSubmit).toHaveBeenCalledWith<[ChartOptions]>({ - ...testDefaultChartOptions, + expect(handleSubmit).toHaveBeenCalledWith< + [ChartBoundaryLevelsFormValues] + >({ boundaryLevel: 3, + dataSetConfigs: testDataSets.map(dataSet => ({ dataSet })), }); }); }); diff --git a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/reducers/chartBuilderReducer.ts b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/reducers/chartBuilderReducer.ts index 165174a8696..707aeb25f9c 100644 --- a/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/reducers/chartBuilderReducer.ts +++ b/src/explore-education-statistics-admin/src/pages/release/datablocks/components/chart/reducers/chartBuilderReducer.ts @@ -14,9 +14,11 @@ import { DataSet } from '@common/modules/charts/types/dataSet'; import { LegendConfiguration } from '@common/modules/charts/types/legend'; import { Chart } from '@common/services/types/blocks'; import deepMerge from 'deepmerge'; +import { isEqual, merge } from 'lodash'; import mapValues from 'lodash/mapValues'; import { useCallback, useMemo } from 'react'; import { Reducer } from 'use-immer'; +import { ChartBoundaryLevelsFormValues } from '../ChartBoundaryLevelsForm'; export interface ChartOptions extends ChartDefinitionOptions { file?: File; @@ -49,6 +51,10 @@ export type ChartBuilderActions = type: 'UPDATE_CHART_LEGEND'; payload: LegendConfiguration; } + | { + type: 'UPDATE_CHART_MAP_BOUNDARY_LEVELS'; + payload: ChartBoundaryLevelsFormValues; + } | { type: 'UPDATE_CHART_MAP_CONFIGURATION'; payload: MapDataSetConfig[]; @@ -225,10 +231,48 @@ export const chartBuilderReducer: Reducer< break; } + case 'UPDATE_CHART_MAP_BOUNDARY_LEVELS': { + draft.options = { + ...(draft.options as ChartOptions), + boundaryLevel: action.payload.boundaryLevel, + }; + + const existingDataSetConfigs = draft.map?.dataSetConfigs ?? []; + const dataSetConfigs = action.payload.dataSetConfigs.map( + // add existing dataGrouping to new MapDataSetConfigs + ({ dataSet, boundaryLevel }) => { + const { dataGrouping } = + existingDataSetConfigs.find(({ dataSet: existingDataSet }) => + isEqual(existingDataSet, dataSet), + )! ?? {}; + + return merge({}, { dataSet, boundaryLevel, dataGrouping }); + }, + ); + + draft.map = { + ...draft.map, + dataSetConfigs, + }; + + break; + } case 'UPDATE_CHART_MAP_CONFIGURATION': { + const existingDataSetConfigs = draft.map?.dataSetConfigs ?? []; + const dataSetConfigs = action.payload.map( + // add existing boundaryLevel to new MapDataSetConfigs + ({ dataSet, dataGrouping }) => { + const { boundaryLevel } = existingDataSetConfigs.find( + ({ dataSet: existingDataSet }) => isEqual(existingDataSet, dataSet), + )!; + + return merge({}, { dataSet, boundaryLevel, dataGrouping }); + }, + ); + draft.map = { ...draft.map, - dataSetConfigs: action.payload, + dataSetConfigs, }; break; @@ -312,6 +356,15 @@ export function useChartBuilderReducer( [dispatch], ); + const updateChartBoundaryLevels = useCallback( + (payload: ChartBoundaryLevelsFormValues) => { + dispatch({ + type: 'UPDATE_CHART_MAP_BOUNDARY_LEVELS', + payload, + }); + }, + [dispatch], + ); const updateChartMapConfiguration = useCallback( (dataSetConfigs: MapDataSetConfig[]) => { dispatch({ @@ -343,6 +396,7 @@ export function useChartBuilderReducer( updateDataSets, updateChartDefinition, updateChartLegend, + updateChartBoundaryLevels, updateChartMapConfiguration, updateChartOptions, updateChartAxis, @@ -353,6 +407,7 @@ export function useChartBuilderReducer( updateChartAxis, updateChartDefinition, updateChartLegend, + updateChartBoundaryLevels, updateChartMapConfiguration, updateChartOptions, resetState, diff --git a/src/explore-education-statistics-common/src/components/form/Form.tsx b/src/explore-education-statistics-common/src/components/form/Form.tsx index 83124b5294e..07aa9424a8e 100644 --- a/src/explore-education-statistics-common/src/components/form/Form.tsx +++ b/src/explore-education-statistics-common/src/components/form/Form.tsx @@ -5,6 +5,7 @@ import { FormIdContextProvider } from '@common/components/form/contexts/FormIdCo import createErrorHelper from '@common/components/form/validation/createErrorHelper'; import useMountedRef from '@common/hooks/useMountedRef'; import useToggle from '@common/hooks/useToggle'; +import { isEqual } from 'lodash'; import camelCase from 'lodash/camelCase'; import React, { FormEvent, @@ -24,6 +25,7 @@ interface Props { submitId?: string; showErrorSummary?: boolean; visuallyHiddenErrorSummary?: boolean; + onChange?: (values: Partial) => Promise | void; onSubmit: (values: TFormValues) => Promise | void; } @@ -52,6 +54,7 @@ export default function Form({ submitId = `${id}-submit`, showErrorSummary = true, visuallyHiddenErrorSummary = false, + onChange, onSubmit, }: Props) { const isMounted = useMountedRef(); @@ -67,10 +70,16 @@ export default function Form({ handleSubmit: submit, } = useFormContext(); - const values = useWatch(); + const values = useWatch(); const previousValues = useRef(values); const previousSubmitCount = useRef(submitCount); + useEffect(() => { + if (!isEqual(previousValues.current, values)) { + onChange?.(values); + } + }, [previousValues, values, onChange]); + const { getAllErrors } = createErrorHelper({ errors, initialTouched, diff --git a/src/explore-education-statistics-common/src/modules/charts/components/MapBlock.tsx b/src/explore-education-statistics-common/src/modules/charts/components/MapBlock.tsx index c35985fa1fe..da74756a818 100644 --- a/src/explore-education-statistics-common/src/modules/charts/components/MapBlock.tsx +++ b/src/explore-education-statistics-common/src/modules/charts/components/MapBlock.tsx @@ -1,14 +1,14 @@ import { SelectOption } from '@common/components/form/FormSelect'; import styles from '@common/modules/charts/components/MapBlock.module.scss'; -import createMapDataSetCategories, { - MapDataSetCategory, -} from '@common/modules/charts/components/utils/createMapDataSetCategories'; -import { LegendDataGroup } from '@common/modules/charts/components/utils/generateLegendDataGroups'; -import MapGeoJSON from '@common/modules/charts/components/MapGeoJSON'; import MapControls from '@common/modules/charts/components/MapControls'; +import MapGeoJSON from '@common/modules/charts/components/MapGeoJSON'; import MapLegend from '@common/modules/charts/components/MapLegend'; import MapSelectedItem from '@common/modules/charts/components/MapSelectedItem'; +import createMapDataSetCategories, { + MapDataSetCategory, +} from '@common/modules/charts/components/utils/createMapDataSetCategories'; import generateFeaturesAndDataGroups from '@common/modules/charts/components/utils/generateFeaturesAndDataGroups'; +import { LegendDataGroup } from '@common/modules/charts/components/utils/generateLegendDataGroups'; import { AxisConfiguration, ChartDefinition, @@ -23,11 +23,11 @@ import getDataSetCategoryConfigs, { } from '@common/modules/charts/util/getDataSetCategoryConfigs'; import { GeoJsonFeatureProperties } from '@common/services/tableBuilderService'; import { Dictionary } from '@common/types'; +import naturalOrderBy from '@common/utils/array/naturalOrderBy'; import classNames from 'classnames'; import { Feature, FeatureCollection, Geometry } from 'geojson'; import { Layer, Path, Polyline } from 'leaflet'; import keyBy from 'lodash/keyBy'; -import orderBy from 'lodash/orderBy'; import React, { useEffect, useMemo, useState } from 'react'; import { MapContainer } from 'react-leaflet'; @@ -160,7 +160,7 @@ export default function MapBlock({ ); const dataSetOptions = useMemo(() => { - return orderBy( + return naturalOrderBy( Object.values(dataSetCategoryConfigs).map(dataSet => ({ label: dataSet.config.label, value: dataSet.dataKey, diff --git a/src/explore-education-statistics-common/src/modules/charts/types/chart.ts b/src/explore-education-statistics-common/src/modules/charts/types/chart.ts index 85c31b779ea..3e23bf25703 100644 --- a/src/explore-education-statistics-common/src/modules/charts/types/chart.ts +++ b/src/explore-education-statistics-common/src/modules/charts/types/chart.ts @@ -102,6 +102,7 @@ export interface DataGroupingConfig { export interface MapDataSetConfig { dataSet: DataSet; dataGrouping: DataGroupingConfig; + boundaryLevel?: number; } export interface MapConfig {