Skip to content

Commit

Permalink
Feat/add insee multiselect and licence groups (#347)
Browse files Browse the repository at this point in the history
* feat: ✨ Add Insee codes to publishingform spatial coverage

* add optgroups to Multiselect

* feat: ✨ groups working with description and code

* refactor: 🎨 improve code readability

* Revert "refactor: 🎨 improve code readability"

This reverts commit 99c7a18.

* fix: revert error commit

* fix: revert error commit

* fix: correct minor syntax error

* fix: remove console.log

* refac: improve code organization

* refactor: 🎨 add types + pass spatialGranularities as a initialOptions to Step2 Spatial granularities

* fix: remove commented code and syntax changes

* fix: put spatial granularities api call in an api file

* fix: edit syntax

* fix: corrected previous emrge error

* fix: various types fixes

* fix: edit wrong types + fix granularities call

* fix: remove double placeholder in Multiselect

* fix: remove duplicate types

* fix: add LocalizedUrl to spatial granularities

* fix: make description in multiselect optionnal + fix granularitiesUrl

* fix: remove console.log

* fix: handle LICENSE_GROUPS not defined + correct wrong type in Multiselect

* fix: another type corrected in Multiselect

* feat: 💄 update optgroup style in Multiselect to match design

* docs: 📝 add changelog

* fix: fix conflicts

* refactor: 🎨 various types of syntax corrections

* fix: display description in Spatial coverage

* style: 🎨 fix indentation in types.ts

* fix: 💄 edit multiselect css to match design expectations

* fix multiselect line-heights and align

* fix: correct line-heights in multiselect
  • Loading branch information
Jorek57 committed Apr 10, 2024
1 parent eb55b32 commit a23da3e
Show file tree
Hide file tree
Showing 13 changed files with 355 additions and 77 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ cypress/screenshots
.lighthouseci/*.html

storybook-static/
venv/pyvenv.cfg
1 change: 1 addition & 0 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@

<meta name="tags-config" content="%7B%22MAX_LENGTH%22%3A%2096%2C%20%22MIN_LENGTH%22%3A%203%7D" />
<meta name="read-only-enabled" content="false">
<meta name="license-groups-options" content="%5B%5B%22Autorit%5Cu00e9s%20administratives%22%2C%20%5B%7B%22value%22%3A%20%22lov2%22%2C%20%22recommended%22%3A%20true%2C%20%22description%22%3A%20%22Recommand%C3%A9e%22%2C%20%22code%22%3A%20%22etalab-2.0%22%7D%2C%20%7B%22value%22%3A%20%22odc-odbl%22%2C%20%22description%22%3A%20%22License%20avec%20obligation%20de%20partage%20%C3%A0%20l%E2%80%99identique%22%2C%20%22code%22%3A%20%22ODbL-1.0%22%7D%2C%20%7B%22value%22%3A%20%22notspecified%22%2C%20%22description%22%3A%20%22Le%20Code%20des%20relations%20entre%20le%20public%20et%20l%E2%80%99administration%20ne%20s%E2%80%99applique%20pas%22%7D%5D%5D%2C%20%5B%22Tous%20producteurs%22%2C%20%5B%7B%22value%22%3A%20%22lov2%22%2C%20%22recommended%22%3A%20true%2C%20%22description%22%3A%20%22Recommand%C3%A9e%22%7D%2C%20%7B%22value%22%3A%20%22cc-by%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22cc-by-sa%22%2C%20%22code%22%3A%20%22CECILL-2.1%22%7D%2C%20%7B%22value%22%3A%20%22cc-zero%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22fr-lo%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22odc-by%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22odc-odbl%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22odc-pdbl%22%2C%20%22code%22%3A%20%22Apache-2.0%22%7D%2C%20%7B%22value%22%3A%20%22other-at%22%7D%2C%20%7B%22value%22%3A%20%22other-open%22%7D%2C%20%7B%22value%22%3A%20%22other-pd%22%7D%2C%20%7B%22value%22%3A%20%22notspecified%22%7D%5D%5D%5D">
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Move Well to datagouv-components [#382](https://github.com/etalab/udata-front/pull/382)
- Add markdown editor [#351](https://github.com/etalab/udata-front/pull/351)
- Upgrade vue dependency [#386](https://github.com/etalab/udata-front/pull/386)
- Add codes and optgroups in Multiselect to display Insee codes and Licence groups [#347] (https://github.com/etalab/udata-front/pull/347)

## 3.5.4 (2024-03-20)

Expand Down
7 changes: 7 additions & 0 deletions udata_front/theme/gouvfr/assets/js/api/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,10 @@ export function publishDataset(dataset) {
export function getFrequenciesUrl () {
return getLocalizedUrl("datasets/frequencies/");
}

/**
* @returns {Promise<Array<import("../types").SpatialGranularity>>}
*/
export function getSpatialGranularities () {
return api.get(getLocalizedUrl("spatial/granularities/")).then(resp => resp.data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ const args = {
values: null,
};

const argsWithExtraDatas = {
placeholder: "Complex select with extra datas",
searchPlaceholder: "Type to search",
emptyPlaceholder: "Select an option",
allOption: "Select something",
initialOptions: Promise.resolve([
{
label: "Option 1",
value: "Option 1",
helper: "Insee: 53350",
description: "French region"
},
{
label: "Option 2",
value: "Option 2",
helper: "Insee: 12005",
description: "French department"
},
]),
values: null,
};

export const SimpleMultiSelect = {
render: (args) => ({
components: { MultiSelect },
Expand Down Expand Up @@ -125,3 +147,16 @@ export const MultipleMultiSelect = {
addAllOption: false,
},
};

export const SimpleMultiSelectWithHelperAndDescription = {
render: (args) => ({
components: { MultiSelect },
setup() {
return { args };
},
template: '<MultiSelect v-bind="args"/>',
}),
args: {
...argsWithExtraDatas,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<div class="multiselect w-100 fr-select-group" :class="selectGroupClass" ref="container" :data-selected="!!selected">
<div>
<label :for="id" :title="explanation">
{{placeholder}}
<Required :required="required"/>
{{ placeholder }}
<Required :required="required" />
<span v-if="explanation" class="fr-icon-information-line" aria-hidden="true"></span>
<span class="fr-hint-text" v-if="hintText">{{ hintText }}</span>
</label>
Expand All @@ -15,15 +15,52 @@
:required="required"
:multiple="multiple"
>
<option
v-for="option in displayedOptions"
:key="option.value"
:value="option.value"
:data-image="option.image"
:hidden="option.hidden"
>
{{option.label}}
</option>
<template v-if="groups">
<template v-for="group in groups">
<optgroup :label="group.name">
<template v-for="option in displayedOptions" :key="option.value">
<option
v-if="group.name == option.group"
:value="option.value"
:data-image="option.image"
:hidden="option.hidden"
:data-helper="option.helper"
:data-description="option.description"
:data-show-icon="option.recommended"
>
{{ option.label }}
</option>
</template>
</optgroup>
</template>
<template v-for="option in displayedOptions" :key="option.value">
<option
v-if="!option.group"
:value="option.value"
:data-image="option.image"
:hidden="option.hidden"
:data-helper="option.helper"
:data-description="option.description"
:data-show-icon="option.recommended"
>
{{ option.label }}
</option>
</template>
</template>
<template v-else>
<option
v-for="option in displayedOptions"
:key="option.value"
:value="option.value"
:data-image="option.image"
:hidden="option.hidden"
:data-helper="option.helper"
:data-description="option.description"
:data-show-icon="option.recommended"
>
{{ option.label }}
</option>
</template>
</select>
</div>
<p :id="validTextId" class="fr-valid-text" v-if="isValid">
Expand Down Expand Up @@ -127,6 +164,23 @@ export default defineComponent({
addNewOption: {
type: Boolean,
default: false,
},
helperLabel: {
type: String,
default: '',
},
groups: {
/** @type {import("vue").PropType<Array<{name: string, values: Array<{code: string, description: string, recommended: boolean, value: string}>}>>} */
type: Array,
default: null
},
onSuggest: {
type: Function,
default: null
},
showDescription: {
type: Boolean,
default: false
}
},
setup(props, { emit }) {
Expand Down Expand Up @@ -181,7 +235,7 @@ export default defineComponent({
* Current options
* @type {import("vue").Ref<Array<import("../../types").MultiSelectOption>>}
*/
const options = ref([]);
const options = ref([]);
/**
* Displayed Options limited to {@link maxOptionsCount}
Expand Down Expand Up @@ -233,15 +287,42 @@ export default defineComponent({
if (!props.listUrl) return options.value;
/**
* @type {import("axios").AxiosResponse<{data: Array}|Array>}
* @typedef {object} Data
* @property {string} id
* @property {String} title
*/
return api.get(props.listUrl)
/**
* @type {Promise<import("axios").AxiosResponse<Array<Data> | {data: Array<Data> }>>}
*/
const request = api.get(props.listUrl)
return request
.then(resp => {
let data = resp.data;
if(!Array.isArray(data)) {
data = data.data;
}
return mapToOption(data);
if (props.groups) {
const groupData = data.map(option => {
const matchingGroup = props.groups.find(group => group.values.some(groupValue => groupValue.value === option.id))
if (matchingGroup) {
const matchingGroupValue = matchingGroup.values.find(groupValue => groupValue.value === option.id);
if (matchingGroupValue) {
return {
...option,
group: matchingGroup.name,
description: matchingGroupValue.description || null,
recommended: matchingGroupValue.recommended,
code: matchingGroupValue.code
};
}
}
return option;
});
return mapToOption(groupData);
} else {
return mapToOption(data);
}
}).catch((error) => {
if (!axios.isCancel(error)) {
toast.error(t("Error getting {type}.", {type: props.placeholder}));
Expand All @@ -255,18 +336,19 @@ export default defineComponent({
* @param {Array} data
* @returns {Array<import("../../types").MultiSelectOption>}
**/
const mapToOption = (data) => data.map((obj) => ({
label: obj.name ?? obj.title ?? obj.text ?? obj?.properties?.name ?? obj.label ?? obj,
value: obj.id ?? obj.text ?? obj.value ?? obj,
image: obj.logo_thumbnail ?? obj.logo ?? obj.image_url ?? obj.image,
hidden: obj.hidden,
selected: !!obj.selected,
}));
/**
* @typedef Suggestion
* @property {string} text
*/
const mapToOption = (data) => data.map((obj) => {
return {
label: obj.name ?? obj.title ?? obj.text ?? obj?.properties?.name ?? obj.label ?? obj,
value: obj.id ?? obj.text ?? obj.value ?? obj,
image: obj.logo_thumbnail ?? obj.logo ?? obj.image_url ?? obj.image,
hidden: obj.hidden,
selected: !!obj.selected,
helper: obj?.code ? props.helperLabel + obj.code : obj?.helper,
description: props.showDescription ? (obj?.description ?? '') : '',
group: obj?.group,
recommended: obj?.recommended
};
});
/**
* Get options from suggest API
Expand All @@ -286,24 +368,26 @@ export default defineComponent({
currentRequest.value = generateCancelToken();
return api
.get(props.suggestUrl, {
params: { q, size: maxOptionsCount },
cancelToken: currentRequest.value.token,
params: { q, size: maxOptionsCount },
cancelToken: currentRequest.value.token,
})
.then((resp) => {
/** @type {Array<Suggestion>} */
const suggestions = resp.data;
return props.onSuggest ? props.onSuggest(resp.data) : resp.data;
})
.then((suggestions) => {
const addQToSuggestion = props.addNewOption && !suggestions.some(suggestion => suggestion.text === q);
if(addQToSuggestion) {
suggestions.push({text: q});
if (addQToSuggestion) {
suggestions.push({ text: q });
}
return mapToOption(suggestions);
})
.catch((error) => {
if (!axios.isCancel(error)) {
toast.error(t("Error getting {type}.", {type: props.placeholder}));
toast.error(t("Error getting {type}.", { type: props.placeholder }));
}
return /** @type {Array<import("../../types").MultiSelectOption>} */([]);
});
return /** @type {Array<import("../../types").MultiSelectOption>} */ ([]);
}
);
};
const suggestAndMapToOption = (q = '') => suggest(q).then(addAllOptionAndMapToOption);
Expand Down Expand Up @@ -370,7 +454,7 @@ export default defineComponent({
* @param {Array<import("../../types").MultiSelectOption>} values
* @returns {Array<import("../../types").MultiSelectOption>}
*/
const setOptions = (values) => {
const setOptions = (values) => {
options.value = values;
return values;
};
Expand Down Expand Up @@ -432,7 +516,7 @@ export default defineComponent({
* @param {string | null} value
* @returns {Promise<string | null>}
*/
const augmentFromValue = (value) => {
const augmentFromValue = (value) => {
let selectedPromise = null;
if (value && props.entityUrl) {
selectedPromise = api
Expand Down Expand Up @@ -463,7 +547,7 @@ export default defineComponent({
}
return "";
});
}
}
/**
* Register event listener to trigger on change on select change event
Expand Down
7 changes: 7 additions & 0 deletions udata_front/theme/gouvfr/assets/js/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ export const resources_min_count_to_show_search = _jsonMeta("resources-min-count
*/
export const markdown = _jsonMeta("markdown-config");

/**
* License groups options configuration.
*/
export const license_groups_options = _jsonMeta("license-groups-options");


/**
* Whether the 'read only mode' feature is enabled or not.
*/
Expand Down Expand Up @@ -318,6 +324,7 @@ export default {
resources_default_page_size,
resources_min_count_to_show_search,
markdown,
license_groups_options,
read_only_enabled,
quality_description_length,
quality_metadata_backend_ignore,
Expand Down

0 comments on commit a23da3e

Please sign in to comment.