Skip to content

Commit 11d3fde

Browse files
authored
IBX-9322: ezobjectrelationlist field allows selecting the same content multiple times (#1409)
1 parent b367a66 commit 11d3fde

File tree

15 files changed

+241
-69
lines changed

15 files changed

+241
-69
lines changed

src/bundle/Resources/public/js/scripts/fieldType/ezobjectrelationlist.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
const itemNodeNameCell = itemNode.querySelector('.ibexa-relations__item-name');
8888

8989
itemNode.dataset.contentId = contentId;
90+
itemNode.dataset.locationId = locationId;
9091
itemNode.querySelector('.ibexa-relations__table-action--remove-item').addEventListener('click', removeItem, false);
9192

9293
itemNodeNameCell.dataset.ibexaUpdateContentId = contentId;
@@ -113,12 +114,12 @@
113114
sourceInput.dispatchEvent(new CustomEvent(EVENT_CUSTOM));
114115
};
115116
const onConfirm = (items) => {
116-
items = excludeDuplicatedItems(items);
117+
const itemsWithoutDuplicate = excludeDuplicatedItems(items);
117118

118-
renderRows(items);
119+
renderRows(itemsWithoutDuplicate);
119120
attachRowsEventHandlers();
120121

121-
selectedItems = [...selectedItems, ...items.map((item) => item.ContentInfo.Content._id)];
122+
selectedItems = [...selectedItems, ...itemsWithoutDuplicate.map((item) => item.ContentInfo.Content._id)];
122123

123124
updateInputValue(selectedItems);
124125
closeUDW();
@@ -128,9 +129,15 @@
128129
};
129130
const openUDW = (event) => {
130131
event.preventDefault();
131-
132+
const selectedItemsRow = fieldContainer.querySelectorAll(SELECTOR_ROW);
132133
const config = JSON.parse(event.currentTarget.dataset.udwConfig);
133134
const limit = parseInt(event.currentTarget.dataset.limit, 10);
135+
const selectedLocations = [...selectedItemsRow].reduce((locationsIds, selectedItemRow) => {
136+
const { locationId } = selectedItemRow.dataset;
137+
const parsedLocationId = parseInt(locationId, 10);
138+
139+
return isNaN(parsedLocationId) ? locationsIds : [...locationsIds, parsedLocationId];
140+
}, []);
134141
const title =
135142
limit === 1
136143
? Translator.trans(
@@ -151,17 +158,15 @@
151158
onCancel: closeUDW,
152159
title,
153160
startingLocationId,
161+
selectedLocations,
162+
isInitLocationsDeselectionBlocked: true,
154163
...config,
155164
multiple: isSingle ? false : selectedItemsLimit !== 1,
156165
multipleItemsLimit: selectedItemsLimit > 1 ? selectedItemsLimit - selectedItems.length : selectedItemsLimit,
157166
}),
158167
);
159168
};
160-
const excludeDuplicatedItems = (items) => {
161-
selectedItemsMap = items.reduce((total, item) => ({ ...total, [item.ContentInfo.Content._id]: item }), selectedItemsMap);
162-
163-
return items.filter((item) => selectedItemsMap[item.ContentInfo.Content._id]);
164-
};
169+
const excludeDuplicatedItems = (items) => items.filter((item) => !selectedItems.includes(item.ContentInfo.Content._id));
165170
const renderRow = (item, index) => {
166171
const { escapeHTML } = ibexa.helpers.text;
167172
const { formatShortDateTime } = ibexa.helpers.timezone;
@@ -304,7 +309,6 @@
304309
updateInputValue(selectedItems);
305310
};
306311
let selectedItems = [...fieldContainer.querySelectorAll(SELECTOR_ROW)].map((row) => parseInt(row.dataset.contentId, 10));
307-
let selectedItemsMap = selectedItems.reduce((total, item) => ({ ...total, [item]: item }), {});
308312

309313
updateAddBtnState();
310314
attachRowsEventHandlers();

src/bundle/Resources/public/scss/ui/modules/universal-discovery/_grid.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.c-grid {
22
height: 100%;
3+
width: 100%;
34

45
&__items-wrapper {
56
padding: 0 calculateRem(16px) calculateRem(16px);

src/bundle/Resources/public/scss/ui/modules/universal-discovery/_selected.locations.item.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,11 @@
5252
.ibexa-icon {
5353
fill: $ibexa-color-dark;
5454
}
55+
56+
&:disabled {
57+
.ibexa-icon {
58+
fill: $ibexa-color-dark-300;
59+
}
60+
}
5561
}
5662
}

src/bundle/Resources/public/scss/ui/modules/universal-discovery/_tab.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,26 @@
3434
flex: 3;
3535
background-color: $ibexa-color-white;
3636
overflow: auto;
37+
38+
&--init-locations-alert-visible {
39+
flex-wrap: wrap;
40+
41+
.ibexa-alert {
42+
margin: 0;
43+
}
44+
45+
.c-udw-tab__main-children {
46+
height: calc(100% - calculateRem(48px));
47+
}
48+
}
49+
}
50+
51+
&__main-children {
52+
display: flex;
53+
}
54+
55+
&__init-locations-alert-container {
56+
width: 100%;
3757
}
3858

3959
&__right-sidebar {

src/bundle/Resources/translations/ibexa_universal_discovery_widget.en.xliff

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@
176176
<target state="new">Filters</target>
177177
<note>key: filters.title</note>
178178
</trans-unit>
179+
<trans-unit id="94d848778c1cab60409c69dcff7e1cb8ea415ffc" resname="init_selected_locations.alert.title">
180+
<source>Items already added to the list are marked as selected and unable to deselect.</source>
181+
<target state="new">Items already added to the list are marked as selected and unable to deselect.</target>
182+
<note>key: init_selected_locations.alert.title</note>
183+
</trans-unit>
179184
<trans-unit id="2d4febe56a341671c069ff47b6e6adfd2bba005c" resname="limitation.pick.error">
180185
<source>Could not fetch content names</source>
181186
<target state="new">Could not fetch content names</target>

src/bundle/Resources/views/themes/admin/ui/field_type/edit/relation_base.html.twig

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@
170170
{% set body_rows = body_rows|merge([{
171171
cols: body_row_cols,
172172
class: 'ibexa-relations__item',
173-
attr: { 'data-content-id': relation.contentInfo.id },
173+
attr: {
174+
'data-content-id': relation.contentInfo.id,
175+
'data-location-id': relation.contentInfo.mainLocationId,
176+
},
174177
}]) %}
175178
{% elseif relation.unauthorized %}
176179
{% set col_raw_unauthorised %}
@@ -194,7 +197,10 @@
194197
{ content: col_raw_order_input, raw: true, attr: { class: 'ibexa-relations__order-wrapper' } }
195198
],
196199
class: 'ibexa-relations__item',
197-
attr: { 'data-content-id': relation.contentId },
200+
attr: {
201+
'data-content-id': relation.contentId,
202+
'data-location-id': relation.contentInfo.mainLocationId,
203+
},
198204
}]) %}
199205
{% endif %}
200206
{% endfor %}

src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ const ContentTableItem = ({ location }) => {
3333
const [multiple] = useContext(MultipleConfigContext);
3434
const rootLocationId = useContext(RootLocationIdContext);
3535
const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href];
36-
const { checkIsSelectable, checkIsSelectionBlocked } = useSelectedLocationsHelpers();
36+
const { checkIsSelectable, checkIsSelectionBlocked, checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
3737
const isNotSelectable = !checkIsSelectable(location);
3838
const isSelectionBlocked = checkIsSelectionBlocked(location);
39+
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
3940
const className = createCssClassNames({
4041
'ibexa-table__row c-content-table-item': true,
4142
'c-content-table-item--marked': markedLocationId === location.id,
@@ -82,7 +83,14 @@ const ContentTableItem = ({ location }) => {
8283
}
8384
};
8485
const renderToggleSelection = () => {
85-
return <ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />;
86+
return (
87+
<ToggleSelection
88+
location={location}
89+
multiple={multiple}
90+
isDisabled={isSelectionBlocked || isDeselectionBlocked}
91+
isHidden={isNotSelectable}
92+
/>
93+
);
8694
};
8795

8896
return (

src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ const FinderLeaf = ({ location }) => {
2424
const contentTypesMap = useContext(ContentTypesMapContext);
2525
const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext);
2626
const [multiple] = useContext(MultipleConfigContext);
27-
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers();
27+
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked, checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
2828
const isSelected = checkIsSelected(location);
2929
const isNotSelectable = !checkIsSelectable(location);
3030
const isSelectionBlocked = checkIsSelectionBlocked(location);
31+
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
3132
const markLocation = ({ nativeEvent }) => {
3233
const isSelectionButtonClicked = nativeEvent.target.closest('.c-udw-toggle-selection');
3334
const isMarkedLocationClicked = location.id === markedLocationId;
@@ -49,7 +50,14 @@ const FinderLeaf = ({ location }) => {
4950
}
5051
};
5152
const renderToggleSelection = () => {
52-
return <ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />;
53+
return (
54+
<ToggleSelection
55+
location={location}
56+
multiple={multiple}
57+
isDisabled={isSelectionBlocked || isDeselectionBlocked}
58+
isHidden={isNotSelectable}
59+
/>
60+
);
5361
};
5462
const className = createCssClassNames({
5563
'c-finder-leaf': true,

src/bundle/ui-dev/src/modules/universal-discovery/components/grid-view/grid.view.item.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ const GridViewItem = ({ location, version }) => {
3131
const containersOnly = useContext(ContainersOnlyContext);
3232
const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href];
3333
const { isContainer } = contentTypeInfo;
34-
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers();
34+
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked, checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
3535
const isSelected = checkIsSelected(location);
3636
const isNotSelectable = !checkIsSelectable(location);
3737
const isSelectionBlocked = checkIsSelectionBlocked(location);
38+
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
3839
const className = createCssClassNames({
3940
'ibexa-grid-view-item': true,
4041
'ibexa-grid-view-item--marked': markedLocationId === location.id,
@@ -70,7 +71,12 @@ const GridViewItem = ({ location, version }) => {
7071
const renderToggleSelection = () => {
7172
return (
7273
<div className="ibexa-grid-view-item__checkbox">
73-
<ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />
74+
<ToggleSelection
75+
location={location}
76+
multiple={multiple}
77+
isDisabled={isSelectionBlocked || isDeselectionBlocked}
78+
isHidden={isNotSelectable}
79+
/>
7480
</div>
7581
);
7682
};

src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.item.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import Thumbnail from '../../../common/thumbnail/thumbnail';
1111

1212
import { SelectedLocationsContext, ContentTypesMapContext } from '../../universal.discovery.module';
1313
import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper';
14+
import { useSelectedLocationsHelpers } from '../../hooks/useSelectedLocationsHelpers';
1415

1516
const SelectedLocationsItem = ({ location, permissions }) => {
1617
const adminUiConfig = getAdminUiConfig();
1718
const Translator = getTranslator();
1819
const refSelectedLocationsItem = useRef(null);
1920
const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext);
21+
const { checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
22+
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
2023
const contentTypesMap = useContext(ContentTypesMapContext);
2124
const clearLabel = Translator.trans(
2225
/*@Desc("Clear selection")*/ 'selected_locations.clear_selection',
@@ -65,6 +68,7 @@ const SelectedLocationsItem = ({ location, permissions }) => {
6568
onClick={removeFromSelection}
6669
title={clearLabel}
6770
data-tooltip-container-selector=".c-udw-tab"
71+
disabled={isDeselectionBlocked}
6872
>
6973
<Icon name="discard" extraClasses="ibexa-icon--tiny-small" />
7074
</button>

0 commit comments

Comments
 (0)