diff --git a/collections/forms/i18n/en.pot b/collections/forms/i18n/en.pot
index dd42956f40..33fbbb3db0 100644
--- a/collections/forms/i18n/en.pot
+++ b/collections/forms/i18n/en.pot
@@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-"POT-Creation-Date: 2024-09-09T12:09:30.724Z\n"
-"PO-Revision-Date: 2024-09-09T12:09:30.724Z\n"
+"POT-Creation-Date: 2024-11-11T07:36:10.587Z\n"
+"PO-Revision-Date: 2024-11-11T07:36:10.588Z\n"
msgid "Upload file"
msgstr "Upload file"
diff --git a/components/sharing-dialog/API.md b/components/sharing-dialog/API.md
index c598050621..03071fedc7 100644
--- a/components/sharing-dialog/API.md
+++ b/components/sharing-dialog/API.md
@@ -16,14 +16,9 @@ import { SharingDialog } from '@dhis2/ui'
|---|---|---|---|---|
|id|string||*|The id of the object to share|
|type|DIALOG_TYPES_LIST||*|The type of object to share|
+|dataSharing|boolean|`false`||Whether to expose the ability to set data sharing (in addition to metadata sharing)|
|dataTest|string|`'dhis2-uicore-sharingdialog'`|||
-|initialSharingSettings|{
"allowPublic": "boolean",
"groups": "objectOf",
"name": "string",
"public": "import {\n ACCESS_NONE,\n ACCESS_VIEW_ONLY,\n ACCESS_VIEW_AND_EDIT,\n DIALOG_TYPES_LIST,\n} from './constants.js' │ import {\n ACCESS_NONE,\n ACCESS_VIEW_ONLY,\n ACCESS_VIEW_AND_EDIT,\n DIALOG_TYPES_LIST,\n} from './constants.js' │ import {\n ACCESS_NONE,\n ACCESS_VIEW_ONLY,\n ACCESS_VIEW_AND_EDIT,\n DIALOG_TYPES_LIST,\n} from './constants.js'",
"users": "objectOf"
}|`{
- name: '',
- allowPublic: true,
- public: ACCESS_NONE,
- groups: {},
- users: {},
-}`||Used to seed the component with data to show whilst loading|
+|initialSharingSettings|object|{
"allowPublic": "boolean",
"name": "string",
"public": {"data": 'ACCESS_NONE' \| 'ACCESS_VIEW_ONLY' \| 'ACCESS_VIEW_AND_EDIT',"metadata": 'ACCESS_NONE' \| 'ACCESS_VIEW_ONLY' \| 'ACCESS_VIEW_AND_EDIT'},
"groups": "objectOf (see below)",
"users": "objectOf (see below)",
}
users and group objects have format of:
{"name": "string",
"id": "string",
"access": {"data": 'ACCESS_NONE' \| 'ACCESS_VIEW_ONLY' \| 'ACCESS_VIEW_AND_EDIT',"metadata": 'ACCESS_NONE' \| 'ACCESS_VIEW_ONLY' \| 'ACCESS_VIEW_AND_EDIT'}}||Used to seed the component with data to show whilst loading|
|onClose|function|`() => {}`|||
|onError|function|`() => {}`|||
|onSave|function|`() => {}`|||
diff --git a/components/sharing-dialog/i18n/en.pot b/components/sharing-dialog/i18n/en.pot
index f6909d3814..dd190099bd 100644
--- a/components/sharing-dialog/i18n/en.pot
+++ b/components/sharing-dialog/i18n/en.pot
@@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-"POT-Creation-Date: 2021-11-25T09:59:40.995Z\n"
-"PO-Revision-Date: 2021-11-25T09:59:40.995Z\n"
+"POT-Creation-Date: 2024-11-07T15:20:34.373Z\n"
+"PO-Revision-Date: 2024-11-07T15:20:34.375Z\n"
msgid "View only"
msgstr "View only"
@@ -14,18 +14,27 @@ msgstr "View only"
msgid "View and edit"
msgstr "View and edit"
+msgid "No access"
+msgstr "No access"
+
msgid "Give access to a user or group"
msgstr "Give access to a user or group"
-msgid "Access level"
-msgstr "Access level"
+msgid "Data access level"
+msgstr "Data access level"
-msgid "Select a level"
-msgstr "Select a level"
+msgid "Choose a level"
+msgstr "Choose a level"
msgid "Not available offline"
msgstr "Not available offline"
+msgid "Metadata access level"
+msgstr "Metadata access level"
+
+msgid "Access level"
+msgstr "Access level"
+
msgid "Give access"
msgstr "Give access"
@@ -38,21 +47,24 @@ msgstr "User / Group"
msgid "All users"
msgstr "All users"
-msgid "No access"
-msgstr "No access"
+msgid "Anyone logged in"
+msgstr "Anyone logged in"
-msgid "Can view"
-msgstr "Can view"
+msgid "User group"
+msgstr "User group"
-msgid "Can view and edit"
-msgstr "Can view and edit"
+msgid "User"
+msgstr "User"
-msgid "Metadata"
-msgstr "Metadata"
+msgid "Data"
+msgstr "Data"
msgid "Remove access"
msgstr "Remove access"
+msgid "Metadata"
+msgstr "Metadata"
+
msgid "User or group"
msgstr "User or group"
diff --git a/components/sharing-dialog/src/access-add/access-add.js b/components/sharing-dialog/src/access-add/access-add.js
index b1d7f6c4f0..56ad8da119 100644
--- a/components/sharing-dialog/src/access-add/access-add.js
+++ b/components/sharing-dialog/src/access-add/access-add.js
@@ -5,15 +5,20 @@ import { SingleSelectField, SingleSelectOption } from '@dhis2-ui/select'
import PropTypes from 'prop-types'
import React, { useState, useContext } from 'react'
import { SharingAutocomplete } from '../autocomplete/index.js'
-import { ACCESS_VIEW_ONLY, ACCESS_VIEW_AND_EDIT } from '../constants.js'
+import {
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+} from '../constants.js'
import { FetchingContext } from '../fetching-context/index.js'
import i18n from '../locales/index.js'
import { Title } from '../text/index.js'
-export const AccessAdd = ({ onAdd }) => {
+export const AccessAdd = ({ onAdd, dataSharing }) => {
const isFetching = useContext(FetchingContext)
const [entity, setEntity] = useState(null)
- const [access, setAccess] = useState('')
+ const [dataAccess, setDataAccess] = useState('')
+ const [metadataAccess, setMetadataAccess] = useState('')
const { isDisconnected: offline } = useDhis2ConnectionStatus()
const onSubmit = (e) => {
@@ -23,14 +28,15 @@ export const AccessAdd = ({ onAdd }) => {
type: entity.type,
id: entity.id,
name: entity.displayName || entity.name,
- access,
+ access: { data: dataAccess, metadata: metadataAccess },
})
setEntity(null)
- setAccess('')
+ setDataAccess('')
+ setMetadataAccess('')
}
- const accessOptions = [
+ const accessOptionsMetadata = [
{
value: ACCESS_VIEW_ONLY,
label: i18n.t('View only'),
@@ -41,41 +47,109 @@ export const AccessAdd = ({ onAdd }) => {
},
]
+ const accessOptionsData = [
+ ...accessOptionsMetadata,
+ {
+ value: ACCESS_NONE,
+ label: i18n.t('No access'),
+ },
+ ]
+
return (
<>
{i18n.t('Give access to a user or group')}
>
)
@@ -99,4 +194,5 @@ export const AccessAdd = ({ onAdd }) => {
AccessAdd.propTypes = {
onAdd: PropTypes.func.isRequired,
+ dataSharing: PropTypes.bool,
}
diff --git a/components/sharing-dialog/src/access-list/access-list.js b/components/sharing-dialog/src/access-list/access-list.js
index 8949cf73ba..e71faa95a6 100644
--- a/components/sharing-dialog/src/access-list/access-list.js
+++ b/components/sharing-dialog/src/access-list/access-list.js
@@ -20,121 +20,178 @@ export const AccessList = ({
allowPublicAccess,
users,
groups,
-}) => (
- <>
- {i18n.t('Users and groups that currently have access')}
-
-
{i18n.t('User / Group')}
-
{i18n.t('Access level')}
-
-
-
- onChange({ type: 'public', access: newAccess })
- }
- />
- {groups.map(({ id, name, access }) => (
+ dataSharing,
+}) => {
+ const accessOptions = [ACCESS_NONE, ACCESS_VIEW_ONLY, ACCESS_VIEW_AND_EDIT]
+ return (
+ <>
+
+ {i18n.t('Users and groups that currently have access')}
+
+
+
+ {i18n.t('User / Group')}
+
+
+ {i18n.t('Access level')}
+
+
+
- onChange({
- type: 'group',
- id,
- access: newAccess,
- })
+ onChange({ type: 'public', access: newAccess })
}
- onRemove={() => onRemove({ type: 'group', id })}
+ dataSharing={dataSharing}
+ allUsersItem={true}
/>
- ))}
- {users.map(
- ({ id, name, access }) =>
- access && (
-
- onChange({
- type: 'user',
- id,
- access: newAccess,
- })
- }
- onRemove={() => onRemove({ type: 'user', id })}
- />
- )
- )}
-
-
- >
-)
+ .header-end-column-data {
+ margin-inline-start: auto;
+ width: 65%;
+ }
+
+ .hea {
+ display: inline-block;
+ margin-inline-start: 8px;
+ }
+
+ .list {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+ }
+ `}
+ >
+ )
+}
AccessList.propTypes = {
allowPublicAccess: PropTypes.bool.isRequired,
+ dataSharing: PropTypes.bool.isRequired,
groups: PropTypes.arrayOf(
PropTypes.shape({
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
+ access: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }),
})
).isRequired,
- publicAccess: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ publicAccess: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
users: PropTypes.arrayOf(
PropTypes.shape({
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ access: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})
diff --git a/components/sharing-dialog/src/access-list/list-item-context.js b/components/sharing-dialog/src/access-list/list-item-context.js
index e996cfee97..894fa4a73a 100644
--- a/components/sharing-dialog/src/access-list/list-item-context.js
+++ b/components/sharing-dialog/src/access-list/list-item-context.js
@@ -2,32 +2,22 @@ import { colors } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
import React from 'react'
import {
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
+ SHARE_TARGET_PUBLIC,
+ SHARE_TARGET_GROUP,
+ SHARE_TARGET_USER,
} from '../constants.js'
import i18n from '../locales/index.js'
-export const ListItemContext = ({ access }) => {
- let message
-
- switch (access) {
- case ACCESS_NONE:
- message = i18n.t('No access')
- break
- case ACCESS_VIEW_ONLY:
- message = i18n.t('Can view')
- break
- case ACCESS_VIEW_AND_EDIT:
- message = i18n.t('Can view and edit')
- break
- default:
- message = ''
- }
+const LABELS = {
+ [SHARE_TARGET_PUBLIC]: i18n.t('Anyone logged in'),
+ [SHARE_TARGET_GROUP]: i18n.t('User group'),
+ [SHARE_TARGET_USER]: i18n.t('User'),
+}
+export const ListItemContext = ({ target, id }) => {
return (
-
- {message}
+ <>
+
{target === SHARE_TARGET_USER ? id : LABELS[target] ?? ''}
-
+ >
)
}
ListItemContext.propTypes = {
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
+ target: PropTypes.oneOf([
+ SHARE_TARGET_PUBLIC,
+ SHARE_TARGET_GROUP,
+ SHARE_TARGET_USER,
]).isRequired,
+ id: PropTypes.string,
}
diff --git a/components/sharing-dialog/src/access-list/list-item.js b/components/sharing-dialog/src/access-list/list-item.js
index 207824b1a4..e9c7809531 100644
--- a/components/sharing-dialog/src/access-list/list-item.js
+++ b/components/sharing-dialog/src/access-list/list-item.js
@@ -19,14 +19,24 @@ import i18n from '../locales/index.js'
import { ListItemContext } from './list-item-context.js'
import { ListItemIcon } from './list-item-icon.js'
+const isRemoveEnabled = ({ dataSharing, accessOtherField }) => {
+ if (!dataSharing) {
+ return true
+ }
+ return accessOtherField === ACCESS_NONE
+}
+
export const ListItem = ({
name,
+ id,
target,
access,
accessOptions,
disabled,
onChange,
onRemove,
+ dataSharing,
+ allUsersItem = false,
}) => {
const isFetching = useContext(FetchingContext)
const { isDisconnected: offline } = useDhis2ConnectionStatus()
@@ -39,38 +49,97 @@ export const ListItem = ({
return (
<>
-
+
-
-
onChange(selected)}
- >
- {accessOptions.map((value) => (
-
- ))}
- {isRemovableTarget(target) && (
-
- )}
-
+
+ {dataSharing && (
+
+
+ onChange({ ...access, data: selected })
+ }
+ >
+ {accessOptions.map((value) => (
+
+ ))}
+ {isRemovableTarget(target) &&
+ isRemoveEnabled({
+ accessOtherField: access.metadata,
+ dataSharing,
+ }) && (
+
+ )}
+
+
+ )}
+
+
+ onChange({ ...access, metadata: selected })
+ }
+ >
+ {accessOptions.map((value) => (
+
+ ))}
+ {isRemovableTarget(target) &&
+ isRemoveEnabled({
+ accessOtherField: access.data,
+ dataSharing,
+ }) && (
+
+ )}
+
+
@@ -78,11 +147,17 @@ export const ListItem = ({
.wrapper {
display: flex;
padding: 4px 8px;
+ justify-content: space-between;
+ }
+
+ .detailsMetadata {
+ display: flex;
+ width: 65%;
}
- .details {
+ .detailsWithData {
display: flex;
- flex: 2;
+ width: 35%;
}
.details-text {
@@ -97,7 +172,16 @@ export const ListItem = ({
padding: 0;
}
+ .selectWrapperMetadata {
+ display: flex;
+ width: 35%;
+ }
+ .selectWrapperWithData {
+ display: flex;
+ width: 65%;
+ }
.select {
+ margin-inline-start: 8px;
flex: 1;
}
`}
@@ -106,14 +190,22 @@ export const ListItem = ({
}
ListItem.propTypes = {
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ access: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
accessOptions: PropTypes.arrayOf(
PropTypes.oneOf([ACCESS_NONE, ACCESS_VIEW_ONLY, ACCESS_VIEW_AND_EDIT])
).isRequired,
+ dataSharing: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
target: PropTypes.oneOf([
SHARE_TARGET_PUBLIC,
@@ -121,6 +213,8 @@ ListItem.propTypes = {
SHARE_TARGET_USER,
]).isRequired,
onChange: PropTypes.func.isRequired,
+ allUsersItem: PropTypes.bool,
disabled: PropTypes.bool,
+ id: PropTypes.string,
onRemove: PropTypes.func,
}
diff --git a/components/sharing-dialog/src/autocomplete/sharing-autocomplete.js b/components/sharing-dialog/src/autocomplete/sharing-autocomplete.js
index 45b45fe2f7..6a64510c20 100644
--- a/components/sharing-dialog/src/autocomplete/sharing-autocomplete.js
+++ b/components/sharing-dialog/src/autocomplete/sharing-autocomplete.js
@@ -70,7 +70,7 @@ export const SharingAutocomplete = ({ selected, onSelection }) => {
return (
item with is visible
When the user sets the access level to
Then the access control should be set to
- And the section should be labeled for
+ And the section should be labeled for
Scenarios:
| initial | target | changed |
diff --git a/components/sharing-dialog/src/features/access-level-change/index.js b/components/sharing-dialog/src/features/access-level-change/index.js
index 099ceecc1c..3245328b1c 100644
--- a/components/sharing-dialog/src/features/access-level-change/index.js
+++ b/components/sharing-dialog/src/features/access-level-change/index.js
@@ -22,7 +22,7 @@ Given('a sharing dialog with all users item with no access is visible', () => {
cy.contains('.details-text', 'All users')
.should('be.visible')
- .contains('No access')
+ .contains('Anyone logged in')
.should('be.visible')
.closest('.wrapper')
.as('all-users-list-item')
@@ -45,7 +45,7 @@ Given('a sharing dialog with user item with view is visible', () => {
cy.contains('.details-text', 'A user')
.should('be.visible')
- .contains('Can view')
+ .contains('user-1')
.should('be.visible')
.closest('.wrapper')
.as('user-list-item')
@@ -68,7 +68,7 @@ Given('a sharing dialog with group item with view is visible', () => {
cy.contains('.details-text', 'A group')
.should('be.visible')
- .contains('Can view')
+ .contains('User group')
.should('be.visible')
.closest('.wrapper')
.as('group-list-item')
@@ -235,26 +235,20 @@ Then('the group access control should be set to view and edit', () => {
* the section should be labeled for
*/
-Then('the all users section should be labeled for view only', () => {
+Then('the all users section should be labeled for all users', () => {
cy.get('@all-users-list-item')
- .contains('.details-text', 'Can view')
+ .contains('.details-text', 'Anyone logged in')
.should('be.visible')
})
-Then('the all users section should be labeled for view and edit', () => {
- cy.get('@all-users-list-item')
- .contains('.details-text', 'Can view and edit')
- .should('be.visible')
-})
-
-Then('the user section should be labeled for view and edit', () => {
+Then('the user section should be labeled for user', () => {
cy.get('@user-list-item')
- .contains('.details-text', 'Can view and edit')
+ .contains('.details-text', 'user-1')
.should('be.visible')
})
-Then('the group section should be labeled for view and edit', () => {
+Then('the group section should be labeled for group', () => {
cy.get('@group-list-item')
- .contains('.details-text', 'Can view and edit')
+ .contains('.details-text', 'User group')
.should('be.visible')
})
diff --git a/components/sharing-dialog/src/features/access-level-remove/index.js b/components/sharing-dialog/src/features/access-level-remove/index.js
index 998224fdba..9029c1560a 100644
--- a/components/sharing-dialog/src/features/access-level-remove/index.js
+++ b/components/sharing-dialog/src/features/access-level-remove/index.js
@@ -21,10 +21,14 @@ Given('a sharing dialog with user item with view is visible', () => {
cy.contains('.details-text', 'A user')
.should('be.visible')
- .contains('Can view')
+ .contains('user-1')
.should('be.visible')
.closest('.wrapper')
.as('user-list-item')
+
+ cy.get('@user-list-item')
+ .contains('[data-test="dhis2-uicore-singleselect"]', 'View only')
+ .should('be.visible')
})
Given('a sharing dialog with user item with view and edit is visible', () => {
@@ -36,10 +40,14 @@ Given('a sharing dialog with user item with view and edit is visible', () => {
cy.contains('.details-text', 'A user')
.should('be.visible')
- .contains('Can view and edit')
+ .contains('user-1')
.should('be.visible')
.closest('.wrapper')
.as('user-list-item')
+
+ cy.get('@user-list-item')
+ .contains('[data-test="dhis2-uicore-singleselect"]', 'View and edit')
+ .should('be.visible')
})
Given('a sharing dialog with group item with view is visible', () => {
@@ -51,10 +59,14 @@ Given('a sharing dialog with group item with view is visible', () => {
cy.contains('.details-text', 'A group')
.should('be.visible')
- .contains('Can view')
+ .contains('User group')
.should('be.visible')
.closest('.wrapper')
.as('group-list-item')
+
+ cy.get('@group-list-item')
+ .contains('[data-test="dhis2-uicore-singleselect"]', 'View only')
+ .should('be.visible')
})
Given('a sharing dialog with group item with view and edit is visible', () => {
@@ -66,10 +78,14 @@ Given('a sharing dialog with group item with view and edit is visible', () => {
cy.contains('.details-text', 'A group')
.should('be.visible')
- .contains('Can view and edit')
+ .contains('User group')
.should('be.visible')
.closest('.wrapper')
.as('group-list-item')
+
+ cy.get('@group-list-item')
+ .contains('[data-test="dhis2-uicore-singleselect"]', 'View and edit')
+ .should('be.visible')
})
/**
diff --git a/components/sharing-dialog/src/features/add-entity/index.js b/components/sharing-dialog/src/features/add-entity/index.js
index b64dbc7d61..8dfea087db 100644
--- a/components/sharing-dialog/src/features/add-entity/index.js
+++ b/components/sharing-dialog/src/features/add-entity/index.js
@@ -42,7 +42,7 @@ When('the user gives user view only access', () => {
cy.get('[placeholder="Search"]').type('A user')
cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A user').click()
- cy.contains('Select a level').click()
+ cy.contains('Choose a level').click()
cy.contains(
'[data-test="dhis2-uicore-singleselectoption"]',
'View only'
@@ -69,7 +69,7 @@ When('the user gives user view and edit access', () => {
cy.get('[placeholder="Search"]').type('A user')
cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A user').click()
- cy.contains('Select a level').click()
+ cy.contains('Choose a level').click()
cy.contains(
'[data-test="dhis2-uicore-singleselectoption"]',
'View and edit'
@@ -96,7 +96,7 @@ When('the user gives group view only access', () => {
cy.get('[placeholder="Search"]').type('A group')
cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A group').click()
- cy.contains('Select a level').click()
+ cy.contains('Choose a level').click()
cy.contains(
'[data-test="dhis2-uicore-singleselectoption"]',
'View only'
@@ -123,7 +123,7 @@ When('the user gives group view and edit access', () => {
cy.get('[placeholder="Search"]').type('A group')
cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A group').click()
- cy.contains('Select a level').click()
+ cy.contains('Choose a level').click()
cy.contains(
'[data-test="dhis2-uicore-singleselectoption"]',
'View and edit'
@@ -152,7 +152,7 @@ Then(
() => {
cy.contains('.wrapper', 'A user').should('be.visible').as('user-item')
- cy.get('@user-item').contains('Can view').should('be.visible')
+ cy.get('@user-item').contains('user-1').should('be.visible')
cy.get('@user-item').contains('View only').should('be.visible')
}
)
@@ -162,7 +162,7 @@ Then(
() => {
cy.contains('.wrapper', 'A user').should('be.visible').as('user-item')
- cy.get('@user-item').contains('Can view and edit').should('be.visible')
+ cy.get('@user-item').contains('user-1').should('be.visible')
cy.get('@user-item').contains('View and edit').should('be.visible')
}
)
@@ -172,7 +172,7 @@ Then(
() => {
cy.contains('.wrapper', 'A group').should('be.visible').as('group-item')
- cy.get('@group-item').contains('Can view').should('be.visible')
+ cy.get('@group-item').contains('User group').should('be.visible')
cy.get('@group-item').contains('View only').should('be.visible')
}
)
@@ -182,7 +182,7 @@ Then(
() => {
cy.contains('.wrapper', 'A group').should('be.visible').as('group-item')
- cy.get('@group-item').contains('Can view and edit').should('be.visible')
+ cy.get('@group-item').contains('User group').should('be.visible')
cy.get('@group-item').contains('View and edit').should('be.visible')
}
)
diff --git a/components/sharing-dialog/src/features/data-sharing.feature b/components/sharing-dialog/src/features/data-sharing.feature
new file mode 100644
index 0000000000..35f8d0f70b
--- /dev/null
+++ b/components/sharing-dialog/src/features/data-sharing.feature
@@ -0,0 +1,91 @@
+Feature: Setting data sharing is possible when sharing dialog is set to include data sharing
+
+
+
+ Scenario Outline: User can add a new entity with specified data and metadata sharing
+ Given a sharing dialog that allows adding user and group entities with data sharing is visible
+ When the user selects a new entity
+ And the user chooses metadata access
+ And the user chooses data access
+ And the user clicks Give access button to give target data access and metadata access
+ Then the should be added to the access list
+ And the should have metadata access
+ And the should have data access
+ And the autocomplete input should be cleared
+
+ Scenarios:
+ | target | datalevel | metadatalevel | datalevelstring | metadatalevelstring |
+ | user | view only | view only | "View only" | "View only" |
+ | group | view only | view only | "View only" | "View only" |
+
+# Scenario: user can change access type
+
+ Scenario Outline: User can change the data access level from to for with metadata access
+ Given a sharing dialog with item with data access and metadata access
+ When the user sets the data access level to , and leaves metadata as
+ Then the data access control should be set to
+ And the metadata access control should remain
+
+ Scenarios:
+ | target | initial | changed | metadata |
+ | all users | "No access" | "View and edit" | "No access" |
+ | user | "View only" | "View and edit" | "No access" |
+ | group | "View and edit" | "View only" | "View only" |
+
+
+# Scenario: user can remove access from data access select if metadata is No access
+
+ Scenario Outline: User can remove access for a if metadata access is No access and data access is
+ Given a sharing dialog with item with data access and "No access" metadata access
+ When the user clicks to remove the access for the from the "Data" access select
+ Then the item should be removed
+
+ Scenarios:
+ | target | data-access-level |
+ | user | "View only" |
+ | group | "View only" |
+
+# Scenario: user can remove access from metadata access select if data is No access
+ Scenario Outline: User can remove access for a if data access is No access and metadata access is
+ Given a sharing dialog with item with "No access" data access and metadata access
+ When the user clicks to remove the access for the from the "Metadata" access select
+ Then the item should be removed
+
+ Scenarios:
+ | target | metadata-access-level |
+ | user | "View only" |
+ | group | "View and edit" |
+
+# Scenario: user cannot remove access from Data access level by if and are both not No access
+
+ Scenario Outline: User cannot remove access for a from Data access when metadata access is set to something other than No access
+ Given a sharing dialog with item with data access and metadata access
+ Then the "Data" access level options do not contain Remove access
+
+ Scenarios:
+ | target | data-access-level | metadata-access-level |
+ | user | "View only" | "View only" |
+ | group | "View and edit" | "View only" |
+
+# Scenario: user cannot remove access from Data access level for all users
+
+ Scenario Outline: User cannot remove access for all users
+ Given a sharing dialog with item with data access and metadata access
+ Then the "Data" access level options do not contain Remove access
+
+ Scenarios:
+ | target | data-access-level | metadata-access-level |
+ | all users | "No access" | "No access" |
+
+# Scenario: user can remove access from data access level by first setting metadata to No Access
+
+ Scenario Outline: User can remove access for a from data access when metadata access is set to No access
+ Given a sharing dialog with item with data access and metadata access
+ When the user sets the metadata access level to No access and leaves data access as
+ And the user clicks to remove the access for the from the access select
+ Then the item should be removed
+
+ Scenarios:
+ | target | data-access-level | metadata-access-level | type |
+ | user | "View only" | "View only" | "Data" |
+ | group | "View only" | "View and edit" | "Data" |
diff --git a/components/sharing-dialog/src/features/data-sharing/index.js b/components/sharing-dialog/src/features/data-sharing/index.js
new file mode 100644
index 0000000000..96d4e634c3
--- /dev/null
+++ b/components/sharing-dialog/src/features/data-sharing/index.js
@@ -0,0 +1,773 @@
+import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
+import {
+ noAccess,
+ searchUser,
+ userNoAccess,
+ searchGroup,
+ getGroupWithDataAndMetadataAccess,
+ getUserWithDataAndMetadataAccess,
+ getAllUsersWithDataAndMetadataAccess,
+ groupNoAccess,
+} from '../fixtures/index.js'
+
+/**
+ * a sharing dialog that allows adding entities is visible
+ */
+
+Given(
+ 'a sharing dialog that allows adding user and group entities with data sharing is visible',
+ () => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: noAccess,
+ })
+
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Give access to a user or group').should('be.visible')
+ cy.contains('Data access level').should('be.visible')
+ cy.contains('Metadata access level').should('be.visible')
+ }
+)
+
+When('the user selects a new user entity', () => {
+ cy.intercept('GET', '/api/38/sharing/search?key=A%20user', {
+ body: searchUser,
+ })
+ cy.get('[placeholder="Search"]').type('A user')
+ cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A user').click()
+})
+
+When('the user selects a new group entity', () => {
+ cy.intercept('GET', '/api/38/sharing/search?key=A%20group', {
+ body: searchGroup,
+ })
+ cy.get('[placeholder="Search"]').type('A group')
+ cy.contains('[data-test="dhis2-uicore-menuitem"]', 'A group').click()
+})
+
+When('the user chooses view only data access', () => {
+ cy.contains('Data access level')
+ .parent()
+ .within(() => {
+ cy.contains('Choose a level').click()
+ })
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselectoption"]',
+ 'View only'
+ ).click()
+})
+
+When('the user chooses view only metadata access', () => {
+ cy.contains('Metadata access level')
+ .parent()
+ .within(() => {
+ cy.contains('Choose a level').click()
+ })
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselectoption"]',
+ 'View only'
+ ).click()
+})
+
+When(
+ 'the user clicks Give access button to give target user {string} data access and {string} metadata access',
+ (dataaccess, metadataaccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getUserWithDataAndMetadataAccess(
+ metadataaccess,
+ dataaccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getUserWithDataAndMetadataAccess(metadataaccess, dataaccess),
+ })
+
+ cy.contains('button', 'Give access').click()
+ }
+)
+
+When(
+ 'the user clicks Give access button to give target group {string} data access and {string} metadata access',
+ (dataaccess, metadataaccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getGroupWithDataAndMetadataAccess(
+ metadataaccess,
+ dataaccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getGroupWithDataAndMetadataAccess(metadataaccess, dataaccess),
+ })
+
+ cy.contains('button', 'Give access').click()
+ }
+)
+
+Then('the user should be added to the access list', () => {
+ cy.contains('.wrapper', 'A user').should('be.visible').as('user-item')
+
+ cy.get('@user-item').contains('user-1').should('be.visible')
+})
+
+Then('the group should be added to the access list', () => {
+ cy.contains('.wrapper', 'A group').should('be.visible').as('group-item')
+
+ cy.get('@group-item').contains('User group').should('be.visible')
+})
+
+Then('the user should have {string} metadata access', (metadataString) => {
+ cy.contains('.wrapper', 'A user').as('user-item')
+
+ cy.get('@user-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadataString).should('be.visible')
+ })
+ })
+})
+
+Then('the group should have {string} metadata access', (metadataString) => {
+ cy.contains('.wrapper', 'A group').as('group-item')
+
+ cy.get('@group-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadataString).should('be.visible')
+ })
+ })
+})
+
+Then('the user should have {string} data access', (dataString) => {
+ cy.contains('.wrapper', 'A user').as('user-item')
+
+ cy.get('@user-item').within(() => {
+ cy.contains('[data-test="dhis2-uicore-singleselect"]', 'Data').within(
+ () => {
+ cy.contains(dataString).should('be.visible')
+ }
+ )
+ })
+})
+
+Then('the group should have {string} data access', (dataString) => {
+ cy.contains('.wrapper', 'A group').as('group-item')
+
+ cy.get('@group-item').within(() => {
+ cy.contains('[data-test="dhis2-uicore-singleselect"]', 'Data').within(
+ () => {
+ cy.contains(dataString).should('be.visible')
+ }
+ )
+ })
+})
+
+Then('the autocomplete input should be cleared', () => {
+ cy.get('form input').invoke('val').should('be.empty')
+})
+
+Given(
+ 'a sharing dialog with user item with {string} data access and {string} metadata access',
+ (datalevel, metadatalevel) => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getUserWithDataAndMetadataAccess(metadatalevel, datalevel),
+ })
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Sharing and access').should('be.visible')
+
+ cy.contains('.details-text', 'A user')
+ .should('be.visible')
+ .contains('user-1')
+ .should('be.visible')
+ .closest('.wrapper')
+ .as('user-list-item')
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(datalevel).should('be.visible')
+ })
+ })
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadatalevel).should('be.visible')
+ })
+ })
+ }
+)
+
+Given(
+ 'a sharing dialog with group item with {string} data access and {string} metadata access',
+ (datalevel, metadatalevel) => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getGroupWithDataAndMetadataAccess(metadatalevel, datalevel),
+ })
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Sharing and access').should('be.visible')
+
+ cy.contains('.details-text', 'A group')
+ .should('be.visible')
+ .contains('User group')
+ .should('be.visible')
+ .closest('.wrapper')
+ .as('group-list-item')
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(datalevel).should('be.visible')
+ })
+ })
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadatalevel).should('be.visible')
+ })
+ })
+ }
+)
+
+Given(
+ 'a sharing dialog with all users item with {string} data access and {string} metadata access',
+ (datalevel, metadatalevel) => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getAllUsersWithDataAndMetadataAccess(
+ metadatalevel,
+ datalevel
+ ),
+ })
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Sharing and access').should('be.visible')
+
+ cy.contains('.details-text', 'All users')
+ .should('be.visible')
+ .contains('Anyone logged in')
+ .should('be.visible')
+ .closest('.wrapper')
+ .as('all-users-list-item')
+
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(datalevel).should('be.visible')
+ })
+ })
+
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadatalevel).should('be.visible')
+ })
+ })
+ }
+)
+
+When(
+ 'the user sets the user data access level to {string}, and leaves metadata as {string}',
+ (dataAccess, metadataAccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getUserWithDataAndMetadataAccess(
+ metadataAccess,
+ dataAccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getUserWithDataAndMetadataAccess(metadataAccess, dataAccess),
+ })
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).click()
+ })
+
+ cy.contains(
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"] [data-test="dhis2-uicore-singleselectoption"]',
+ dataAccess
+ )
+ .should('be.visible')
+ .click()
+
+ // Menu should be closed before continuing
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
+ 'not.exist'
+ )
+ }
+)
+
+When(
+ 'the user sets the group data access level to {string}, and leaves metadata as {string}',
+ (dataAccess, metadataAccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getGroupWithDataAndMetadataAccess(
+ metadataAccess,
+ dataAccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getGroupWithDataAndMetadataAccess(metadataAccess, dataAccess),
+ })
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).click()
+ })
+
+ cy.contains(
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"] [data-test="dhis2-uicore-singleselectoption"]',
+ dataAccess
+ )
+ .should('be.visible')
+ .click()
+
+ // Menu should be closed before continuing
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
+ 'not.exist'
+ )
+ }
+)
+
+When(
+ 'the user sets the all users data access level to {string}, and leaves metadata as {string}',
+ (dataAccess, metadataAccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getAllUsersWithDataAndMetadataAccess(
+ metadataAccess,
+ dataAccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getAllUsersWithDataAndMetadataAccess(
+ metadataAccess,
+ dataAccess
+ ),
+ })
+
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).click()
+ })
+
+ cy.contains(
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"] [data-test="dhis2-uicore-singleselectoption"]',
+ dataAccess
+ )
+ .should('be.visible')
+ .click()
+
+ // Menu should be closed before continuing
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
+ 'not.exist'
+ )
+ }
+)
+
+Then(
+ 'the user data access control should be set to {string}',
+ (newDataAccess) => {
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(newDataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+Then(
+ 'the group data access control should be set to {string}',
+ (newDataAccess) => {
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(newDataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+Then(
+ 'the all users data access control should be set to {string}',
+ (newDataAccess) => {
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(newDataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+Then(
+ 'the user metadata access control should remain {string}',
+ (metadataAccess) => {
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+Then(
+ 'the group metadata access control should remain {string}',
+ (metadataAccess) => {
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+Then(
+ 'the all users metadata access control should remain {string}',
+ (metadataAccess) => {
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains(metadataAccess).should('be.visible')
+ })
+ })
+ }
+)
+
+// a sharing dialog with item with for data and No access for metadata is visible
+
+When(
+ 'a sharing dialog with user item with {string} for data and No access for metadata is visible',
+ (dataAccess) => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getUserWithDataAndMetadataAccess('No access', dataAccess),
+ })
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Sharing and access').should('be.visible')
+
+ cy.contains('.details-text', 'A user')
+ .should('be.visible')
+ .contains('user-1')
+ .should('be.visible')
+ .closest('.wrapper')
+ .as('user-list-item')
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(dataAccess).should('be.visible')
+ })
+ })
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains('No access').should('be.visible')
+ })
+ })
+ }
+)
+
+When(
+ 'a sharing dialog with group item with {string} for data and No access for metadata is visible',
+ (dataAccess) => {
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getGroupWithDataAndMetadataAccess('No access', dataAccess),
+ })
+ cy.visitStory('sharing-dialog', 'data')
+ cy.contains('Sharing and access').should('be.visible')
+
+ cy.contains('.details-text', 'A group')
+ .should('be.visible')
+ .contains('User group')
+ .should('be.visible')
+ .closest('.wrapper')
+ .as('group-list-item')
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Data'
+ ).within(() => {
+ cy.contains(dataAccess).should('be.visible')
+ })
+ })
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).within(() => {
+ cy.contains('No access').should('be.visible')
+ })
+ })
+ }
+)
+
+When(
+ 'the user clicks to remove the access for the user from the {string} access select',
+ (type) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: userNoAccess.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: userNoAccess,
+ })
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains('[data-test="dhis2-uicore-singleselect"]', type).click()
+ })
+
+ cy.contains('Remove access').should('be.visible').click()
+ }
+)
+
+When(
+ 'the user clicks to remove the access for the group from the {string} access select',
+ (type) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: groupNoAccess.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: groupNoAccess,
+ })
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains('[data-test="dhis2-uicore-singleselect"]', type).click()
+ })
+
+ cy.contains('Remove access').should('be.visible').click()
+ }
+)
+
+Then('the user item should be removed', () => {
+ cy.contains('.details-text', 'A user').should('not.exist')
+})
+
+Then('the group item should be removed', () => {
+ cy.contains('.details-text', 'A group').should('not.exist')
+})
+
+When(
+ 'the user sets the user metadata access level to No access and leaves data access as {string}',
+ (dataAccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getUserWithDataAndMetadataAccess(
+ 'No access',
+ dataAccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getUserWithDataAndMetadataAccess('No access', dataAccess),
+ })
+
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).click()
+ })
+
+ cy.contains(
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"] [data-test="dhis2-uicore-singleselectoption"]',
+ 'No access'
+ )
+ .should('be.visible')
+ .click()
+
+ // Menu should be closed before continuing
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
+ 'not.exist'
+ )
+ }
+)
+
+When(
+ 'the user sets the group metadata access level to No access and leaves data access as {string}',
+ (dataAccess) => {
+ cy.intercept(
+ 'PUT',
+ '/api/38/sharing?type=visualization&id=id',
+ (req) => {
+ const expected = {
+ object: getGroupWithDataAndMetadataAccess(
+ 'No access',
+ dataAccess
+ )?.object,
+ }
+ expect(req.body).to.deep.equal(expected)
+ req.reply({ statusCode: 200 })
+ }
+ )
+ cy.intercept('GET', '/api/38/sharing?type=visualization&id=id', {
+ body: getGroupWithDataAndMetadataAccess('No access', dataAccess),
+ })
+
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ 'Metadata'
+ ).click()
+ })
+
+ cy.contains(
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"] [data-test="dhis2-uicore-singleselectoption"]',
+ 'No access'
+ )
+ .should('be.visible')
+ .click()
+
+ // Menu should be closed before continuing
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
+ 'not.exist'
+ )
+ }
+)
+
+Then(
+ 'the user {string} access level options do not contain Remove access',
+ (sharingType) => {
+ cy.get('@user-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ sharingType
+ ).click()
+ })
+
+ cy.contains('Remove access').should('not.exist')
+ }
+)
+
+Then(
+ 'the group {string} access level options do not contain Remove access',
+ (sharingType) => {
+ cy.get('@group-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ sharingType
+ ).click()
+ })
+
+ cy.contains('Remove access').should('not.exist')
+ }
+)
+
+Then(
+ 'the all users {string} access level options do not contain Remove access',
+ (sharingType) => {
+ cy.get('@all-users-list-item').within(() => {
+ cy.contains(
+ '[data-test="dhis2-uicore-singleselect"]',
+ sharingType
+ ).click()
+ })
+
+ cy.contains('Remove access').should('not.exist')
+ }
+)
diff --git a/components/sharing-dialog/src/features/fixtures/get-object-data-metadata-access.js b/components/sharing-dialog/src/features/fixtures/get-object-data-metadata-access.js
new file mode 100644
index 0000000000..144fa4c196
--- /dev/null
+++ b/components/sharing-dialog/src/features/fixtures/get-object-data-metadata-access.js
@@ -0,0 +1,84 @@
+const convertAccess = (access) => {
+ if (access === 'View only') {
+ return 'r-'
+ }
+ if (access === 'View and edit') {
+ return 'rw'
+ }
+ return '--'
+}
+
+export const getUserWithDataAndMetadataAccess = (
+ metadataaccess,
+ dataaccess
+) => ({
+ meta: {
+ allowExternalAccess: false,
+ allowPublicAccess: false,
+ },
+ object: {
+ id: 'id',
+ name: '',
+ displayName: '',
+ externalAccess: false,
+ publicAccess: '--------',
+ userAccesses: [
+ {
+ id: 'user-1',
+ name: 'A user',
+ access: `${convertAccess(metadataaccess)}${convertAccess(
+ dataaccess
+ )}----`,
+ },
+ ],
+ userGroupAccesses: [],
+ },
+})
+
+export const getGroupWithDataAndMetadataAccess = (
+ metadataaccess,
+ dataaccess
+) => ({
+ meta: {
+ allowExternalAccess: false,
+ allowPublicAccess: false,
+ },
+ object: {
+ id: 'id',
+ name: '',
+ displayName: '',
+ externalAccess: false,
+ publicAccess: '--------',
+ userAccesses: [],
+ userGroupAccesses: [
+ {
+ id: 'group-1',
+ name: 'A group',
+ access: `${convertAccess(metadataaccess)}${convertAccess(
+ dataaccess
+ )}----`,
+ },
+ ],
+ },
+})
+
+export const getAllUsersWithDataAndMetadataAccess = (
+ metadataaccess,
+ dataaccess
+) => ({
+ meta: {
+ allowExternalAccess: true,
+ allowPublicAccess: true,
+ },
+ object: {
+ id: 'id',
+ name: '',
+ displayName: '',
+ externalAccess: false,
+ publicAccess: `${convertAccess(metadataaccess)}${convertAccess(
+ dataaccess
+ )}----`,
+ userAccesses: [],
+ userGroupAccesses: [],
+ },
+})
diff --git a/components/sharing-dialog/src/features/fixtures/index.js b/components/sharing-dialog/src/features/fixtures/index.js
index c29b40e121..b6b41b2039 100644
--- a/components/sharing-dialog/src/features/fixtures/index.js
+++ b/components/sharing-dialog/src/features/fixtures/index.js
@@ -4,6 +4,11 @@ import allUsersViewEditAccess from './all-users-view-edit-access.json'
import dashboardSharing from './dashboard-sharing.json'
import dashboards from './dashboards.json'
import disabledAccess from './disabled-access.json'
+import {
+ getUserWithDataAndMetadataAccess,
+ getGroupWithDataAndMetadataAccess,
+ getAllUsersWithDataAndMetadataAccess,
+} from './get-object-data-metadata-access.js'
import groupNoAccess from './group-no-access.json'
import groupViewAccess from './group-view-access.json'
import groupViewEditAccess from './group-view-edit-access.json'
@@ -34,4 +39,7 @@ export {
userViewEditAccess,
withDisplayname,
withoutDisplayname,
+ getGroupWithDataAndMetadataAccess,
+ getUserWithDataAndMetadataAccess,
+ getAllUsersWithDataAndMetadataAccess,
}
diff --git a/components/sharing-dialog/src/helpers/__tests__/helpers.test.js b/components/sharing-dialog/src/helpers/__tests__/helpers.test.js
index a1d95b1d9a..7c8679dce4 100644
--- a/components/sharing-dialog/src/helpers/__tests__/helpers.test.js
+++ b/components/sharing-dialog/src/helpers/__tests__/helpers.test.js
@@ -6,70 +6,85 @@ import {
SHARE_TARGET_PUBLIC,
} from '../../constants.js'
import {
- convertAccessToConstant,
- convertConstantToAccess,
+ convertAccessToConstantObject,
+ convertConstantObjectToAccess,
isRemovableTarget,
} from '../helpers.js'
describe('helpers', () => {
- describe('convertAccessToConstant', () => {
+ describe('convertAccessToConstantObject', () => {
it('disallows access if the access string is undefined', () => {
- expect(convertAccessToConstant()).toEqual(ACCESS_NONE)
+ const NO_ACCESS_OBJECT = {
+ data: ACCESS_NONE,
+ metadata: ACCESS_NONE,
+ }
+ expect(convertAccessToConstantObject()).toEqual(NO_ACCESS_OBJECT)
})
it('disallows access if the access string is invalid', () => {
- expect(convertAccessToConstant('invalid-access-string')).toEqual(
- ACCESS_NONE
- )
+ const NO_ACCESS_OBJECT = {
+ data: ACCESS_NONE,
+ metadata: ACCESS_NONE,
+ }
+ expect(
+ convertAccessToConstantObject('invalid-access-string')
+ ).toEqual(NO_ACCESS_OBJECT)
})
const cases = [
- ['--------', ACCESS_NONE],
- ['r-------', ACCESS_VIEW_ONLY],
- ['r-r-----', ACCESS_VIEW_ONLY],
- ['rw------', ACCESS_VIEW_AND_EDIT],
- ['rwrw----', ACCESS_VIEW_AND_EDIT],
+ ['--------', { data: ACCESS_NONE, metadata: ACCESS_NONE }],
+ ['r-------', { data: ACCESS_NONE, metadata: ACCESS_VIEW_ONLY }],
+ [
+ 'r-r-----',
+ { data: ACCESS_VIEW_ONLY, metadata: ACCESS_VIEW_ONLY },
+ ],
+ ['rw------', { data: ACCESS_NONE, metadata: ACCESS_VIEW_AND_EDIT }],
+ [
+ 'rwrw----',
+ { data: ACCESS_VIEW_AND_EDIT, metadata: ACCESS_VIEW_AND_EDIT },
+ ],
]
it.each(cases)(
'parses the metadata portion of the access string correctly for %s',
(accessString, accessConstant) => {
- expect(convertAccessToConstant(accessString)).toEqual(
+ expect(convertAccessToConstantObject(accessString)).toEqual(
accessConstant
)
}
)
})
- describe('convertConstantToAccess', () => {
+ describe('convertConstantObjectToAccess', () => {
it('returns the default access string if the access constant is not recognised', () => {
const expected = '--------'
- expect(convertConstantToAccess('NOT_RECOGNISED')).toEqual(expected)
+ expect(
+ convertConstantObjectToAccess({
+ data: 'NOT_RECOGNISED',
+ metadata: 'NOT_RECOGNISED',
+ })
+ ).toEqual(expected)
})
const cases = [
- [ACCESS_NONE, '--------', false],
- [ACCESS_VIEW_ONLY, 'r-------', true],
- [ACCESS_VIEW_AND_EDIT, 'rw------', true],
+ [{ data: ACCESS_NONE, metadata: ACCESS_NONE }, '--------'],
+ [{ data: ACCESS_NONE, metadata: ACCESS_VIEW_ONLY }, 'r-------'],
+ [{ data: ACCESS_NONE, metadata: ACCESS_VIEW_AND_EDIT }, 'rw------'],
+ [{ data: ACCESS_VIEW_ONLY, metadata: ACCESS_NONE }, '--r-----'],
+ [
+ { data: ACCESS_VIEW_AND_EDIT, metadata: ACCESS_VIEW_AND_EDIT },
+ 'rwrw----',
+ ],
]
it.each(cases)(
'returns the correct metadata access string for %s',
(accessConstant, accessString) => {
- expect(convertConstantToAccess(accessConstant)).toEqual(
+ expect(convertConstantObjectToAccess(accessConstant)).toEqual(
accessString
)
}
)
-
- it.each(cases)(
- 'returns the correct boolean value for %s',
- (accessConstant, accessString, accessBoolean) => {
- expect(convertConstantToAccess(accessConstant, true)).toEqual(
- accessBoolean
- )
- }
- )
})
describe('isRemovableTarget', () => {
diff --git a/components/sharing-dialog/src/helpers/helpers.js b/components/sharing-dialog/src/helpers/helpers.js
index 5fcd25529c..56a928c082 100644
--- a/components/sharing-dialog/src/helpers/helpers.js
+++ b/components/sharing-dialog/src/helpers/helpers.js
@@ -41,7 +41,7 @@ export const debounce = (func, wait, immediate) => {
* Access and constant conversion
*/
-export const convertAccessToConstant = (access) => {
+const convertAccessStringToConstant = (access) => {
if (access === undefined) {
return ACCESS_NONE
}
@@ -59,26 +59,48 @@ export const convertAccessToConstant = (access) => {
}
}
-export const convertConstantToAccess = (constant, useBoolean) => {
+export const convertAccessToConstantObject = (accessString) => {
+ if (typeof accessString === 'boolean') {
+ return {
+ data: ACCESS_NONE,
+ metadata: convertAccessStringToConstant(accessString),
+ }
+ }
+ const metadataAccessString = accessString?.substring(0, 2)
+ const dataAccessString = accessString?.substring(2, 4)
+
+ return {
+ data: convertAccessStringToConstant(dataAccessString),
+ metadata: convertAccessStringToConstant(metadataAccessString),
+ }
+}
+
+export const convertConstantToAccessString = (constant) => {
switch (constant) {
case ACCESS_NONE:
- return useBoolean ? false : '--------'
+ return '--'
case ACCESS_VIEW_ONLY:
- return useBoolean ? true : 'r-------'
+ return 'r-'
case ACCESS_VIEW_AND_EDIT:
- return useBoolean ? true : 'rw------'
+ return 'rw'
default:
- return useBoolean ? false : '--------'
+ return '--'
}
}
+export const convertConstantObjectToAccess = (accessObject) => {
+ return `${convertConstantToAccessString(
+ accessObject.metadata
+ )}${convertConstantToAccessString(accessObject.data)}----`
+}
+
/**
* Replaces access property with constants used internally
*/
export const replaceAccessWithConstant = ({ access, ...rest }) => ({
...rest,
- access: convertAccessToConstant(access),
+ access: convertAccessToConstantObject(access),
})
/**
@@ -102,7 +124,7 @@ export const createOnChangePayload = ({ object, type, access, id }) => {
const data = {
object: {
...object,
- publicAccess: convertConstantToAccess(access),
+ publicAccess: convertConstantObjectToAccess(access),
},
}
return data
@@ -115,7 +137,7 @@ export const createOnChangePayload = ({ object, type, access, id }) => {
return {
...group,
- access: convertConstantToAccess(access),
+ access: convertConstantObjectToAccess(access),
}
})
const data = {
@@ -124,6 +146,7 @@ export const createOnChangePayload = ({ object, type, access, id }) => {
userGroupAccesses,
},
}
+ console.log(data)
return data
}
case 'user': {
@@ -134,7 +157,7 @@ export const createOnChangePayload = ({ object, type, access, id }) => {
return {
...user,
- access: convertConstantToAccess(access),
+ access: convertConstantObjectToAccess(access),
}
})
const data = {
@@ -159,7 +182,7 @@ export const createOnAddPayload = ({ object, type, id, access, name }) => {
{
id,
name,
- access: convertConstantToAccess(access),
+ access: convertConstantObjectToAccess(access),
},
],
},
@@ -175,7 +198,7 @@ export const createOnAddPayload = ({ object, type, id, access, name }) => {
{
id,
name,
- access: convertConstantToAccess(access),
+ access: convertConstantObjectToAccess(access),
},
],
},
diff --git a/components/sharing-dialog/src/sharing-dialog.e2e.stories.js b/components/sharing-dialog/src/sharing-dialog.e2e.stories.js
index 2410271765..94b19b743c 100644
--- a/components/sharing-dialog/src/sharing-dialog.e2e.stories.js
+++ b/components/sharing-dialog/src/sharing-dialog.e2e.stories.js
@@ -23,3 +23,9 @@ export const Dashboard = () => (
)
+
+export const Data = () => (
+
+
+
+)
diff --git a/components/sharing-dialog/src/sharing-dialog.js b/components/sharing-dialog/src/sharing-dialog.js
index 21490420ab..e5412d72c8 100644
--- a/components/sharing-dialog/src/sharing-dialog.js
+++ b/components/sharing-dialog/src/sharing-dialog.js
@@ -9,7 +9,7 @@ import {
} from './constants.js'
import { FetchingContext } from './fetching-context/index.js'
import {
- convertAccessToConstant,
+ convertAccessToConstantObject,
replaceAccessWithConstant,
createOnChangePayload,
createOnAddPayload,
@@ -39,13 +39,49 @@ const mutation = {
}
const emptyFunction = () => {}
+
const defaultInitialSharingSettings = {
name: '',
allowPublic: true,
- public: ACCESS_NONE,
- groups: {},
- users: {},
+ public: { data: ACCESS_NONE, metadata: ACCESS_NONE },
+ groups: [],
+ users: [],
}
+
+const mapInitialSharingSettings = (originalSharingSettings) => {
+ const mappedSharingSettings = { ...originalSharingSettings }
+ if (
+ originalSharingSettings.public &&
+ typeof originalSharingSettings.public === 'string'
+ ) {
+ mappedSharingSettings.public = {
+ data: ACCESS_NONE,
+ metadata: originalSharingSettings.public,
+ }
+ }
+ mappedSharingSettings.groups = originalSharingSettings.groups.map(
+ (group) => {
+ if (group.access && typeof group.access === 'string') {
+ return {
+ ...group,
+ access: { data: ACCESS_NONE, metadata: group.access },
+ }
+ }
+ return group
+ }
+ )
+ mappedSharingSettings.users = originalSharingSettings.users.map((user) => {
+ if (user.access && typeof user.access === 'string') {
+ return {
+ ...user,
+ access: { data: ACCESS_NONE, metadata: user.access },
+ }
+ }
+ return user
+ })
+ return mappedSharingSettings
+}
+
export const SharingDialog = ({
id,
type,
@@ -54,8 +90,12 @@ export const SharingDialog = ({
onSave = emptyFunction,
initialSharingSettings = defaultInitialSharingSettings,
dataTest = 'dhis2-uicore-sharingdialog',
+ dataSharing = false,
}) => {
const { show: showError } = useAlert((error) => error, { critical: true })
+ const mappedInitialSharingSettings = mapInitialSharingSettings(
+ initialSharingSettings
+ )
/**
* Data fetching
@@ -112,12 +152,15 @@ export const SharingDialog = ({
id={id}
users={users}
groups={groups}
- publicAccess={initialSharingSettings.public}
- allowPublicAccess={initialSharingSettings.allowPublic}
+ publicAccess={mappedInitialSharingSettings.public}
+ allowPublicAccess={
+ mappedInitialSharingSettings.allowPublic
+ }
type={type}
onAdd={() => {}}
onChange={() => {}}
onRemove={() => {}}
+ dataSharing={dataSharing}
/>
@@ -125,7 +168,7 @@ export const SharingDialog = ({
}
const { object, meta } = data.sharing
- const publicAccess = convertAccessToConstant(object.publicAccess)
+ const publicAccess = convertAccessToConstantObject(object.publicAccess)
const users = object.userAccesses.map(replaceAccessWithConstant)
const groups = object.userGroupAccesses.map(replaceAccessWithConstant)
@@ -180,6 +223,7 @@ export const SharingDialog = ({
onAdd={onAdd}
onChange={onChange}
onRemove={onRemove}
+ dataSharing={dataSharing}
/>
@@ -191,28 +235,80 @@ SharingDialog.propTypes = {
id: PropTypes.string.isRequired,
/** The type of object to share */
type: PropTypes.oneOf(DIALOG_TYPES_LIST).isRequired,
+ /** Whether to expose the ability to set data sharing (in addition to metadata sharing) */
+ dataSharing: PropTypes.bool,
dataTest: PropTypes.string,
/** Used to seed the component with data to show whilst loading */
initialSharingSettings: PropTypes.shape({
allowPublic: PropTypes.bool.isRequired,
groups: PropTypes.objectOf(
PropTypes.shape({
- access: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
+ access: PropTypes.oneOfType([
+ PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }),
+ PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ ]),
})
),
name: PropTypes.string,
- public: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
+ public: PropTypes.oneOfType([
+ PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }),
+ PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
]),
users: PropTypes.objectOf(
PropTypes.shape({
- access: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
+ access: PropTypes.oneOfType([
+ PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }),
+ PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ ]),
})
),
}),
diff --git a/components/sharing-dialog/src/sharing-dialog.prod.stories.js b/components/sharing-dialog/src/sharing-dialog.prod.stories.js
index 72181e4479..232f09dd9b 100644
--- a/components/sharing-dialog/src/sharing-dialog.prod.stories.js
+++ b/components/sharing-dialog/src/sharing-dialog.prod.stories.js
@@ -129,7 +129,7 @@ const customDataWithUserGroupAccesses = {
{
id: 'user-1',
name: 'Kvist',
- access: 'rw------',
+ access: 'rwr-----',
},
],
userGroupAccesses: [
@@ -200,6 +200,15 @@ export const WithUserAndGroupAccesses = (args) => (
)
WithUserAndGroupAccesses.storyName = 'With user and group accesses'
+export const WithDataUserAndGroupAccesses = (args) => (
+
+
+
+)
+WithDataUserAndGroupAccesses.storyName =
+ 'With data sharing, user and group accesses'
+WithDataUserAndGroupAccesses.args = { dataSharing: true }
+
export const ForDashboard = (args) => (
diff --git a/components/sharing-dialog/src/tabs/tabbed-content.js b/components/sharing-dialog/src/tabs/tabbed-content.js
index 82c6d9cf73..76de4ffec9 100644
--- a/components/sharing-dialog/src/tabs/tabbed-content.js
+++ b/components/sharing-dialog/src/tabs/tabbed-content.js
@@ -23,6 +23,7 @@ export const TabbedContent = ({
onAdd,
onChange,
onRemove,
+ dataSharing,
}) => {
const [activeTabIndex, setActiveTabIndex] = useState(0)
@@ -46,7 +47,10 @@ export const TabbedContent = ({
{activeTabIndex === 0 && (
<>
-
+
>
)}
@@ -70,7 +75,7 @@ export const TabbedContent = ({
return (
<>
-
+
>
)
@@ -85,31 +91,53 @@ export const TabbedContent = ({
TabbedContent.propTypes = {
allowPublicAccess: PropTypes.bool.isRequired,
+ dataSharing: PropTypes.bool.isRequired,
groups: PropTypes.arrayOf(
PropTypes.shape({
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ access: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})
).isRequired,
id: PropTypes.string.isRequired,
- publicAccess: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ publicAccess: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
type: PropTypes.oneOf(DIALOG_TYPES_LIST).isRequired,
users: PropTypes.arrayOf(
PropTypes.shape({
- access: PropTypes.oneOf([
- ACCESS_NONE,
- ACCESS_VIEW_ONLY,
- ACCESS_VIEW_AND_EDIT,
- ]).isRequired,
+ access: PropTypes.shape({
+ data: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ metadata: PropTypes.oneOf([
+ ACCESS_NONE,
+ ACCESS_VIEW_ONLY,
+ ACCESS_VIEW_AND_EDIT,
+ ]),
+ }).isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})
diff --git a/components/sharing-dialog/types/index.d.ts b/components/sharing-dialog/types/index.d.ts
index 1cdecbddf6..90b68873ec 100644
--- a/components/sharing-dialog/types/index.d.ts
+++ b/components/sharing-dialog/types/index.d.ts
@@ -1,13 +1,19 @@
import * as React from 'react'
-import { ModalOnCloseEventHandler } from '../modal'
+import { LayerBackdropClickHandler } from '@dhis2-ui/layer'
-interface SharingObject {
- access: string
+type ModalOnCloseEventHandler = LayerBackdropClickHandler
+
+type SharingAccess = {
+ data: 'ACCESS_NONE' | 'ACCESS_VIEW_ONLY' | 'ACCESS_VIEW_AND_EDIT'
+ metadata: 'ACCESS_NONE' | 'ACCESS_VIEW_ONLY' | 'ACCESS_VIEW_AND_EDIT'
+}
+
+type SharingObject = {
+ access: SharingAccess
id: string
name: string
}
-type SharingPublic = 'ACCESS_NONE' | 'ACCESS_VIEW_ONLY' | 'ACCESS_VIEW_AND_EDIT'
type SharingType =
| 'aggregateDataExchange'
| 'apiToken'
@@ -67,10 +73,10 @@ type SharingType =
export interface SharingDialogInitialSharingSettings {
allowPublic: boolean
- groups?: SharingObject
+ groups?: SharingObject[]
name?: string
- public?: SharingPublic
- users?: SharingObject
+ public?: SharingAccess
+ users?: SharingObject[]
}
export interface SharingDialogProps {
@@ -86,6 +92,7 @@ export interface SharingDialogProps {
* Used to seed the component with data to show whilst loading
*/
initialSharingSettings?: SharingDialogInitialSharingSettings
+ dataSharing?: boolean
onClose?: ModalOnCloseEventHandler
onError?: (error: any) => void
onSave?: () => void