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

Feature/pxweb2 198 add ability to change codelist on variable #336

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6707232
Add API call and state cleaning for new codelists
SjurSutterudSagen Oct 23, 2024
3f755e1
WIP: Removed changes for codelist defaultSelection
SjurSutterudSagen Oct 29, 2024
49d18e0
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 8, 2024
189683a
Add logic for changing codelists
SjurSutterudSagen Nov 11, 2024
55357e5
Tiny tmp fix
SjurSutterudSagen Nov 11, 2024
25f5682
Remove unneeded reset
SjurSutterudSagen Nov 12, 2024
f019bb1
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 13, 2024
13f490f
Remove reduntant check
SjurSutterudSagen Nov 14, 2024
dd3e56d
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 20, 2024
6b941f1
Revert new types for selection mapper
SjurSutterudSagen Nov 20, 2024
57052c0
Add basic reset of selection when changing language
SjurSutterudSagen Nov 20, 2024
fea9201
Add comments for reset on language change
SjurSutterudSagen Nov 21, 2024
65f89cb
Remove old comment
SjurSutterudSagen Nov 21, 2024
d38a2f2
Remove som interal state in VariableBoxSelect
SjurSutterudSagen Nov 25, 2024
4fb0bc4
Bugfix: Make the function no longer update the original array
SjurSutterudSagen Nov 25, 2024
5b5aaf6
Update the handleCodeChange to handle errors
SjurSutterudSagen Nov 25, 2024
0692ebf
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 25, 2024
60af903
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 26, 2024
78822ed
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Nov 27, 2024
ebcf16f
Fix a bug due to race condition with virtuoso
SjurSutterudSagen Nov 27, 2024
5e3d8c0
Update the previous fix
SjurSutterudSagen Nov 29, 2024
eb3f9f3
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Dec 2, 2024
6acdf7a
Linting changes
SjurSutterudSagen Dec 2, 2024
3626cd4
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Dec 4, 2024
ddba283
Add basic fading when making api-call for new codelist
SjurSutterudSagen Dec 4, 2024
1f2e2af
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Dec 5, 2024
4a7a914
Left from merge from main
SjurSutterudSagen Dec 5, 2024
3fdef4f
Remove unused style code in selection.tsx
SjurSutterudSagen Dec 5, 2024
05c54c4
Add resetting of variablebox when changing codelist
SjurSutterudSagen Dec 5, 2024
f31da99
Add new storybook location to gitignore
SjurSutterudSagen Dec 6, 2024
4f3c2c5
Merge branch 'main' into feature/PXWEB2-198-add-ability-to-change-cod…
SjurSutterudSagen Dec 6, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
dist
tmp
/out-tsc
storybook-static

# dependencies
node_modules
Expand Down
10 changes: 8 additions & 2 deletions packages/pxweb2-ui/src/lib/components/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useRef } from 'react';
import { useState, useRef, useEffect } from 'react';
import cl from 'clsx';

import classes from './Search.module.scss';
Expand All @@ -7,6 +7,7 @@ import { Label } from '../Typography/Label/Label';
import { Button } from '../Button/Button';

export interface SearchProps {
value?: string;
variant: 'default' | 'inVariableBox';
labelText?: string;
searchPlaceHolder?: string;
Expand All @@ -18,6 +19,7 @@ export interface SearchProps {
}

export function Search({
value = '',
variant,
labelText,
searchPlaceHolder,
Expand All @@ -28,9 +30,13 @@ export function Search({
onChange,
...rest
}: SearchProps) {
const [inputValue, setInputValue] = useState('');
const [inputValue, setInputValue] = useState(value);
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
setInputValue(value);
}, [value]);

const handleClear = () => {
onChange && onChange('');
setInputValue('');
Expand Down
11 changes: 6 additions & 5 deletions packages/pxweb2-ui/src/lib/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,17 @@ function VariableBoxSelect({
const cssClasses = className.length > 0 ? ' ' + className : '';

const [isModalOpen, setModalOpen] = useState<boolean>(false);

const [selectedItem, setSelectedItem] = useState<SelectOption | undefined>(
selectedOption,
);
const [clickedItem, setClickedItem] = useState<SelectOption | undefined>(
selectedOption,
);

const selectedItem: SelectOption | undefined = selectedOption;

const handleOpenModal = () => {
setModalOpen(true);

// Reset clicked item to selected item, incase user made changes and then closed the modal
setClickedItem(selectedItem);
};

function handleRadioChange(e: React.ChangeEvent<HTMLInputElement>) {
Expand All @@ -189,7 +191,6 @@ function VariableBoxSelect({
const handleCloseModal = (updated: boolean) => {
setModalOpen(false);
if (updated) {
setSelectedItem(clickedItem);
onChange(clickedItem);
} else {
setClickedItem(selectedItem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export function VariableBoxContent({
const [currentFocusedItemIndex, setCurrentFocusedItemIndex] = useState<
number | null
>(null);

const [items, setItems] = useState<{ type: string; value?: Value }[]>([]);

const valuesOnlyList = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -104,6 +103,10 @@ export function VariableBoxContent({
useEffect(() => {
const newItems: { type: string; value?: Value }[] = [];

if (!valuesToRender || valuesToRender.length === 0) {
return;
}

if (hasSevenOrMoreValues) {
newItems.push({ type: 'search' });
}
Expand Down Expand Up @@ -251,10 +254,41 @@ export function VariableBoxContent({
}
};

const handleChangingCodeListInVariableBox = (
selectedItem: SelectOption | undefined,
varId: string,
virtuosoRef: React.RefObject<VirtuosoHandle>,
) => {
// Call the parent function to change the code list
onChangeCodeList(selectedItem, varId);

// Reset search state
if (search !== '') {
setSearch('');
}

// Reset the scroll show/hide state
if (scrollingDown) {
setScrollingDown(false);
}

// Reset the virtuoso list to the top/first item
if (virtuosoRef.current) {
virtuosoRef.current.scrollToIndex(0);
}
};

// Modify the itemRenderer to assign IDs and tabIndex
const itemRenderer = (items: any, index: number) => {
const item = items[index];

// There is a race condition with virtuoso where item can be undefined
// Virtuoso will also complain if we return null or similar, so we return an empty div
// This empty div will be removed by the Virtuoso component, and won't be rendered
if (item === undefined) {
return <div></div>;
}

if (item.type === 'search') {
return (
<div
Expand All @@ -263,6 +297,7 @@ export function VariableBoxContent({
className={classes['focusableItem']}
>
<Search
value={search}
onChange={(value: string) => {
setSearch(value);
if (value === '') {
Expand Down Expand Up @@ -416,7 +451,13 @@ export function VariableBoxContent({
)}
options={mappedCodeLists}
selectedOption={selectedCodeListOrUndefined}
onChange={(selectedItem) => onChangeCodeList(selectedItem, varId)}
onChange={(selectedItem) =>
handleChangingCodeListInVariableBox(
selectedItem,
varId,
virtuosoRef,
)
}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
flex-direction: column;
gap: fixed.$spacing-3;
}

.fadeVariableList {
opacity: 0.6;
transition: 'opacity 0.3s ease-in-out';
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import cl from 'clsx';

import styles from './VariableList.module.scss';
import { SelectedVBValues, VariableBox } from '../VariableBox/VariableBox';
import { PxTableMetadata } from '../../shared-types/pxTableMetadata';
Expand All @@ -8,6 +10,7 @@ export type VariableListProps = {
pxTableMetadata: PxTableMetadata | null;
isLoadingMetadata: boolean;
hasLoadedDefaultSelection: boolean;
isChangingCodeList: boolean;
selectedVBValues: SelectedVBValues[];

// TODO: Optimise here? Duplicate with props in VariableBox
Expand All @@ -27,13 +30,18 @@ export function VariableList({
pxTableMetadata,
isLoadingMetadata,
hasLoadedDefaultSelection,
isChangingCodeList = false,
selectedVBValues,
handleCodeListChange,
handleCheckboxChange,
handleMixedCheckboxChange,
}: VariableListProps) {
return (
<div className={styles.variableList}>
<div
className={cl(styles.variableList, {
[styles.fadeVariableList]: isChangingCodeList,
})}
>
{!isLoadingMetadata &&
hasLoadedDefaultSelection &&
pxTableMetadata &&
Expand Down
101 changes: 69 additions & 32 deletions packages/pxweb2/src/app/components/Selection/Selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ function addSelectedCodeListToVariable(
if (currentVariable) {
newSelectedValues = selectedValuesArr.map((variable) => {
if (variable.id === varId) {
variable.selectedCodeList = selectedItem.value;
variable.values = []; // Always reset values when changing codelist
return {
...variable,
selectedCodeList: selectedItem.value,
values: [], // Always reset values when changing codelist
};
}

return variable;
Expand Down Expand Up @@ -81,6 +84,22 @@ function addValueToNewVariable(
return newSelectedValues;
}

async function getCodeListValues(id: string, lang: string): Promise<Value[]> {
let values: Value[] = [];

await TableService.getTableCodeListById(id, lang)
.then((response) => {
response.values.forEach((value) => {
values = [...values, { code: value.code, label: value.label }];
});
})
.catch((error) => {
throw new Error(error);
});

return values;
}

function removeValueOfVariable(
selectedValuesArr: SelectedVBValues[],
varId: string,
Expand Down Expand Up @@ -232,15 +251,18 @@ export function Selection({
const { selectedVBValues, setSelectedVBValues } = useVariables();
const variables = useVariables();
const [errorMsg, setErrorMsg] = useState('');
const [pxTableMetaToRender, setPxTableMetaToRender] =
// Metadata to render in the UI
useState<PxTableMetadata | null>(null);
const { i18n, t } = useTranslation();
const { hasLoadedDefaultSelection } = useVariables();
const { isLoadingMetadata } = useVariables();
const { pxTableMetadata, setPxTableMetadata } = useVariables();

const [pxTableMetaToRender, setPxTableMetaToRender] =
// Metadata to render in the UI
useState<PxTableMetadata | null>(null);
const [prevTableId, setPrevTableId] = useState('');
const [isFadingVariableList, setIsFadingVariableList] = useState(false);

// Needed to know when the language has changed, so we can reload the default selection
const [prevLang, setPrevLang] = useState('');

useEffect(() => {
let shouldGetDefaultSelection = !hasLoadedDefaultSelection;
Expand All @@ -249,11 +271,18 @@ export function Selection({
return;
}

if (prevTableId === '' || prevTableId !== selectedTabId) {
// If the table has changed, or the language has changed, we need to reload the default selection
if (
prevTableId === '' ||
prevTableId !== selectedTabId ||
prevLang !== i18n.resolvedLanguage
) {
variables.setHasLoadedDefaultSelection(false);
shouldGetDefaultSelection = true;
setPrevTableId(selectedTabId);
setPrevLang(i18n.resolvedLanguage || '');
}

if (isLoadingMetadata === false) {
variables.setIsLoadingMetadata(true);
}
Expand Down Expand Up @@ -314,18 +343,24 @@ export function Selection({
setPxTableMetaToRender(structuredClone(pxTableMetadata));
}

function handleCodeListChange(
async function handleCodeListChange(
selectedItem: SelectOption | undefined,
varId: string,
) {
const prevSelectedValues = structuredClone(selectedVBValues);
const currentVariableMetadata = pxTableMetaToRender?.variables.find(
(variable) => variable.id === varId,
);
const currentVariable = prevSelectedValues.find(
const currentSelectedVariable = prevSelectedValues.find(
(variable) => variable.id === varId,
);
const currentCodeList = currentVariable?.selectedCodeList;
const lang = i18n.resolvedLanguage;

// No language, do nothing
if (lang === undefined) {
return;
}
const currentCodeList = currentSelectedVariable?.selectedCodeList;

// No new selection made, do nothing
if (!selectedItem || selectedItem.value === currentCodeList) {
Expand All @@ -347,20 +382,33 @@ export function Selection({
const newMappedSelectedCodeList =
mapCodeListToSelectOption(newSelectedCodeList);
const newSelectedValues = addSelectedCodeListToVariable(
currentVariable,
currentSelectedVariable,
prevSelectedValues,
varId,
newMappedSelectedCodeList,
);

updateAndSyncVBValues(newSelectedValues);
setIsFadingVariableList(true);

// TODO: This currently returns dummy data until we have the API call setup for it
const valuesForChosenCodeList: Value[] = getCodeListValues(
// Get the values for the chosen code list
const valuesForChosenCodeList: Value[] = await getCodeListValues(
newMappedSelectedCodeList.value,
);
lang,
)
.finally(() => {
setIsFadingVariableList(false);
})
.catch((error) => {
console.error(
'Could not get values for code list: ' +
newMappedSelectedCodeList.value +
' ' +
error,
);
return [];
});

if (pxTableMetaToRender === null || valuesForChosenCodeList.length < 1) {
if (valuesForChosenCodeList.length < 1) {
return;
}

Expand All @@ -386,7 +434,10 @@ export function Selection({
});
});

setPxTableMetaToRender(newPxTableMetaToRender);
// update the state
updateAndSyncVBValues(newSelectedValues);
setPxTableMetadata(newPxTableMetaToRender);
setPxTableMetaToRender(null);
}

const handleCheckboxChange = (varId: string, value: Value['code']) => {
Expand Down Expand Up @@ -469,27 +520,13 @@ export function Selection({
variables.syncVariablesAndValues(selectedVBValues);
}

const getCodeListValues = (id: string) => {
/* TODO: Implement querying the API */
const dummyValues: Value[] = [
{ code: 'Dummy Code 1', label: 'Dummy Value 1' },
{ code: '01', label: '01 Stockholm county' },
{ code: 'Dummy Code 2', label: 'Dummy Value 2' },
{ code: 'Dummy Code 3', label: 'Dummy Value 3' },
{ code: 'Dummy Code 4', label: 'Dummy Value 4' },
{ code: 'Dummy Code 5', label: 'Dummy Value 5' },
{ code: 'Dummy Code 6', label: 'Dummy Value 6' },
{ code: 'Dummy Code 7', label: 'Dummy Value 7' },
];

return dummyValues;
};
const drawerFilter = (
<VariableList
pxTableMetadata={pxTableMetaToRender}
selectedVBValues={selectedVBValues}
isLoadingMetadata={isLoadingMetadata}
hasLoadedDefaultSelection={hasLoadedDefaultSelection}
isChangingCodeList={isFadingVariableList}
handleCodeListChange={handleCodeListChange}
handleCheckboxChange={handleCheckboxChange}
handleMixedCheckboxChange={handleMixedCheckboxChange}
Expand Down
Loading
Loading