diff --git a/platform/core/src/extensions/ExtensionManager.ts b/platform/core/src/extensions/ExtensionManager.ts
index b000bb07f..a41b0e8b9 100644
--- a/platform/core/src/extensions/ExtensionManager.ts
+++ b/platform/core/src/extensions/ExtensionManager.ts
@@ -354,7 +354,7 @@ export default class ExtensionManager extends PubSubService {
* @param dataSourceName the data source name
* @returns the data source definition
*/
- getDataSourceDef = dataSourceName => {
+ getDataSourceDefinition = dataSourceName => {
if (dataSourceName === undefined) {
// Default to the activeDataSource
dataSourceName = this.activeDataSource;
@@ -363,6 +363,13 @@ export default class ExtensionManager extends PubSubService {
return this.dataSourceDefs[dataSourceName];
};
+ /**
+ * Gets the data source definition for the active data source.
+ */
+ getActiveDataSourceDefinition = () => {
+ return this.getDataSourceDefinition(this.activeDataSource);
+ };
+
/**
* @private
* @param {string} moduleType
diff --git a/platform/core/src/types/DataSourceConfigurationAPI.ts b/platform/core/src/types/DataSourceConfigurationAPI.ts
new file mode 100644
index 000000000..146f4a285
--- /dev/null
+++ b/platform/core/src/types/DataSourceConfigurationAPI.ts
@@ -0,0 +1,68 @@
+export interface BaseDataSourceConfigurationAPIItem {
+ id: string;
+ name: string;
+}
+
+/**
+ * The interface to use to configure an associated data source. Typically an
+ * instance of this interface is associated with a data source that the instance
+ * understands and can alter the data source's configuration.
+ */
+export interface BaseDataSourceConfigurationAPI {
+ /**
+ * Gets the i18n labels (i.e. the i18n lookup keys) for each of the configurable items
+ * of the data source configuration API.
+ * For example, for the Google Cloud Healthcare API, this would be
+ * ['Project', 'Location', 'Data set', 'DICOM store'].
+ * Besides the configurable item labels themselves, several other string look ups
+ * are used base on EACH of the labels returned by this method.
+ * For instance, for the label {itemLabel}, the following strings are fetched for
+ * translation...
+ * 1. `No {itemLabel} available`
+ * - used to indicate no such items are available
+ * - for example, for Google, `No Project available` would be 'No projects available'
+ * 2. `Select {itemLabel}`
+ * - used to direct selection of the item
+ * - for example, for Google, `Select Project` would be 'Select a project'
+ * 3. `Error fetching {itemLabel} list`
+ * - used to indicate an error occurred fetching the list of items
+ * - usually accompanied by the error itself
+ * - for example, for Google, `Error fetching Project list` would be 'Error fetching projects'
+ * 4. `Search {itemLabel} list`
+ * - used as the placeholder text for filtering a list of items
+ * - for example, for Google, `Search Project list` would be 'Search projects'
+ */
+ getItemLabels(): Array;
+
+ /**
+ * Initializes the data source configuration API and returns the top-level sub-items
+ * that can be chosen to begin the process of configuring the data source.
+ * For example, for the Google Cloud Healthcare API, this would perform the initial request
+ * to fetch the top level projects for the logged in user account.
+ */
+ initialize(): Promise>;
+
+ /**
+ * Sets the current path item and returns the sub-items of that item
+ * that can be further chosen to configure a data source.
+ * When setting the last configurable item of the data source (path), this method
+ * returns an empty list AND configures the active data source with the selected
+ * items path.
+ * For example, for the Google Cloud Healthcare API, this would take the current item
+ * (say a data set) and queries and returns its sub-items (i.e. all of the DICOM stores
+ * contained in that data set). Furthermore, whenever the item to set is a DICOM store,
+ * the Google Cloud Healthcare API implementation would update the OHIF data source
+ * associated with this instance to point to that DICOM store.
+ * @param item the item to set as current
+ */
+ setCurrentItem(
+ item: BaseDataSourceConfigurationAPIItem
+ ): Promise>;
+
+ /**
+ * Gets the list of items currently configured for the data source associated with
+ * this API instance. The resultant array must be the same length as the result of
+ * `getItemLabels`.
+ */
+ getConfiguredItems(): Promise>;
+}
diff --git a/platform/core/src/types/index.ts b/platform/core/src/types/index.ts
index cdbce68e7..d2a39b83b 100644
--- a/platform/core/src/types/index.ts
+++ b/platform/core/src/types/index.ts
@@ -3,6 +3,10 @@ import * as HangingProtocol from './HangingProtocol';
import Services from './Services';
import Hotkey from '../classes/Hotkey';
import { DataSourceDefinition } from './DataSource';
+import {
+ BaseDataSourceConfigurationAPI,
+ BaseDataSourceConfigurationAPIItem,
+} from './DataSourceConfigurationAPI';
export * from '../services/CustomizationService/types';
// Separate out some generic types
@@ -19,4 +23,12 @@ export * from './Color';
* Export the types used within the various services and managers, but
* not the services/managers themselves, which are exported at the top level.
*/
-export { Extensions, HangingProtocol, Services, Hotkey, DataSourceDefinition };
+export {
+ Extensions,
+ HangingProtocol,
+ Services,
+ Hotkey,
+ DataSourceDefinition,
+ BaseDataSourceConfigurationAPI,
+ BaseDataSourceConfigurationAPIItem,
+};
diff --git a/platform/docs/docs/assets/img/data-source-configuration-ui.png b/platform/docs/docs/assets/img/data-source-configuration-ui.png
new file mode 100644
index 000000000..f04956e07
Binary files /dev/null and b/platform/docs/docs/assets/img/data-source-configuration-ui.png differ
diff --git a/platform/docs/docs/configuration/dataSources/configuration-ui.md b/platform/docs/docs/configuration/dataSources/configuration-ui.md
new file mode 100644
index 000000000..15f3b0bcb
--- /dev/null
+++ b/platform/docs/docs/configuration/dataSources/configuration-ui.md
@@ -0,0 +1,172 @@
+---
+sidebar_position: 6
+sidebar_label: Configuration UI
+---
+
+# Configuration UI
+
+OHIF provides for a generic mechanism for configuring a data source. This is
+most useful for those organizations with several data sources
+that share common (path) hierarchies. For example, an organization may have several DICOM stores
+in the Google Cloud Healthcare realm where each is organized into various projects,
+location, data sets and DICOM stores.
+
+By implementing the `BaseDataSourceConfigurationAPI` and
+`BaseDataSourceConfigurationAPIItem` in an [OHIF extension](../../platform/extensions/index.md), a data source can
+be made configurable via the generic UI as is depicted below for a
+Google Cloud Healthcare data source.
+
+
+
+## `BaseDataSourceConfigurationAPIItem` interface
+
+Each (path) item of a data source is represented by an instance of this interface.
+At the very least each of these items must expose two properties:
+
+|Property |Description|
+|---------|-----------|
+|id|a string that uniquely identifies the item|
+|name|a human readable name for the item|
+
+Note that information such as where in the path hierarchy the item exists
+has been omitted, but can be added in any concrete class that might implement this
+interface. For example, the the Google Cloud Healthcare implementation of this
+interface (`GoogleCloudDataSourceConfigurationAPIItem`) adds an `itemType`
+(i.e. projects, locations, datasets, or dicomStores) and `url`.
+
+## `BaseDataSourceConfigurationAPI` interface
+
+The implementation of this interface is at the heart of the configuration process.
+It possesses several methods for building up a data source path based on various
+`BaseDataSourceConfigurationAPIItem` objects that are set via calls to the `setCurrentItem`
+method.
+
+The constructor for the concrete class implementation should accept whatever
+parameters are necessary for configuring the data source. One argument
+to the constructor must be the string identifying the name of the data source
+to be configured. Furthermore, considering that the `ExtensionManager` possesses
+API to configure and update data sources, it too will likely be an argument to
+the constructor. See [Creation via Customization Module](#creation-via-customization-module)
+for more information on how the constructor is invoked via a factory method.
+
+For an example implementation of this interface see `GoogleCloudDataSourceConfigurationAPI`.
+
+### Interface Methods
+
+Each of the following subsections lists a method of the interface with a description
+detailing what the method should do.
+
+#### `getItemLabels`
+
+Gets the i18n labels (i.e. the i18n lookup keys) for each of the configurable items
+of the data source configuration API. For example, for the Google Cloud Healthcare
+API, this would be `['Project', 'Location', 'Data set', 'DICOM store']`.
+
+Besides the configurable item labels themselves, several other string look ups
+are used base on EACH of the labels returned by this method.
+For instance, for the label `{itemLabel}``, the following strings are fetched for
+translation...
+1. `No {itemLabel} available`
+ - used to indicate no such items are available
+ - for example, for Google, `No Project available` would be 'No projects available'
+2. `Select {itemLabel}`
+ - used to direct selection of the item
+ - for example, for Google, `Select Project` would be 'Select a project'
+3. `Error fetching {itemLabel} list`
+ - used to indicate an error occurred fetching the list of items
+ - usually accompanied by the error itself
+ - for example, for Google, `Error fetching Project list` would be 'Error fetching projects'
+4. `Search {itemLabel} list`
+ - used as the placeholder text for filtering a list of items
+ - for example, for Google, `Search Project list` would be 'Search projects'
+
+#### `initialize`
+
+Initializes the cloud server API and returns the top-level sub-items
+that can be chosen to begin the process of configuring a data source.
+For example, for the Google Cloud Healthcare API, this would perform the initial request
+to fetch the top level projects for the logged in user account.
+
+#### `setCurrentItem`
+
+Sets the current path item that is passed as an argument to the method and
+returns the sub-items of that item
+that can be further chosen to configure a data source.
+When setting the last configurable item of the data source (path), this method
+returns an empty list AND configures the active data source with the selected
+items path.
+
+For example, for the Google Cloud Healthcare API, this would take the current item
+(say a data set) and queries and returns its sub-items (i.e. all of the DICOM stores
+contained in that data set). Furthermore, whenever the item to set is a DICOM store,
+the Google Cloud Healthcare API implementation would update the OHIF data source
+associated with this instance to point to that DICOM store.
+
+#### `getConfiguredItems`
+
+Gets the list of items currently configured for the data source associated with
+this API instance. The resultant array must be the same length as the result of
+`getItemLabels`. Furthermore the items returned should correspond (index-wise)
+with the labels returned from `getItemLabels`.
+
+## Creation via Customization Module
+
+The generic UI (i.e. `DataSourceConfigurationComponent`) uses the
+[OHIF UI customization service](../../platform/services/ui/customization-service.md) to
+instantiate the `BaseDataSourceConfigurationAPI` instance to configure a data source.
+
+A UI configurable data source should have a `configurationAPI` field as part of
+its `configuration` in the OHIF config file. The `configurationAPI` value is the
+customization id of the customization module that provides the factory method
+to instantiate the `BaseDataSourceConfigurationAPI` instance.
+
+For example, the following is a snippet of a Google Cloud Healthcare data source configuration.
+
+```js
+ dataSources: [
+ {
+ namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
+ sourceName: 'google-dicomweb',
+ configuration: {
+ name: 'GCP',
+ wadoUriRoot: 'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/...',
+ ...
+ configurationAPI: 'ohif.dataSourceConfigurationAPI.google',
+ ...
+ },
+ },
+ ]
+```
+
+This suggests that the factory method is provided by the `'ohif.dataSourceConfigurationAPI.google'`
+customization module. That customization module is provided by the `default` extension's
+`getCustomizationModule` and looks something like the following snippet of code. Notice that
+the factory method's name MUST be `factory` and accept one argument - the data source name.
+Furthermore note how the constructor is invoked with anything required by the concrete configuration
+API class.
+
+```js
+export default function getCustomizationModule({
+ servicesManager,
+ extensionManager,
+}) {
+ return [
+ {
+ name: 'default',
+ value: [
+ {
+ // The factory for creating an instance of a BaseDataSourceConfigurationAPI for Google Cloud Healthcare
+ id: 'ohif.dataSourceConfigurationAPI.google',
+ factory: (dataSourceName: string) =>
+ new GoogleCloudDataSourceConfigurationAPI(
+ dataSourceName,
+ servicesManager,
+ extensionManager
+ ),
+ },
+ ],
+ },
+ ];
+}
+
+```
diff --git a/platform/docs/docs/configuration/dataSources/static-files.md b/platform/docs/docs/configuration/dataSources/static-files.md
index 296a87996..820e20a1a 100644
--- a/platform/docs/docs/configuration/dataSources/static-files.md
+++ b/platform/docs/docs/configuration/dataSources/static-files.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 5
sidebar_label: Static Files
---
diff --git a/platform/i18n/src/locales/en-US/DataSourceConfiguration.json b/platform/i18n/src/locales/en-US/DataSourceConfiguration.json
new file mode 100644
index 000000000..07af42104
--- /dev/null
+++ b/platform/i18n/src/locales/en-US/DataSourceConfiguration.json
@@ -0,0 +1,24 @@
+{
+ "Configure Data Source":"Configure Data Source",
+ "Data set": "Data set",
+ "DICOM store": "DICOM store",
+ "Location": "Location",
+ "Project": "Project",
+ "Error fetching Data set list": "Error fetching data sets",
+ "Error fetching DICOM store list": "Error fetching DICOM stores",
+ "Error fetching Location list": "Error fetching locations",
+ "Error fetching Project list": "Error fetching projects",
+ "No Project available": "No projects available",
+ "No Location available": "No locations available",
+ "No Data set available": "No data sets available",
+ "No DICOM store available": "No DICOM stores available",
+ "Select": "Select",
+ "Search Data set list": "Search data sets",
+ "Search DICOM store list": "Search DICOM stores",
+ "Search Location list": "Search locations",
+ "Search Project list": "Search projects",
+ "Select Data set": "Select a data Set",
+ "Select DICOM store": "Select a DICOM store",
+ "Select Location": "Select a location",
+ "Select Project": "Select a project"
+}
diff --git a/platform/i18n/src/locales/en-US/index.js b/platform/i18n/src/locales/en-US/index.js
index 037f807cd..d8b19e6f8 100644
--- a/platform/i18n/src/locales/en-US/index.js
+++ b/platform/i18n/src/locales/en-US/index.js
@@ -2,6 +2,7 @@ import AboutModal from './AboutModal.json';
import Buttons from './Buttons.json';
import CineDialog from './CineDialog.json';
import Common from './Common.json';
+import DataSourceConfiguration from './DataSourceConfiguration.json';
import DatePicker from './DatePicker.json';
import Header from './Header.json';
import MeasurementTable from './MeasurementTable.json';
@@ -18,6 +19,7 @@ export default {
Buttons,
CineDialog,
Common,
+ DataSourceConfiguration,
DatePicker,
Header,
MeasurementTable,
diff --git a/platform/ui/src/assets/icons/status-untracked.svg b/platform/ui/src/assets/icons/status-untracked.svg
index cf347223f..a4186231a 100644
--- a/platform/ui/src/assets/icons/status-untracked.svg
+++ b/platform/ui/src/assets/icons/status-untracked.svg
@@ -1,6 +1,6 @@
diff --git a/platform/ui/src/components/Button/__stories__/button.stories.mdx b/platform/ui/src/components/Button/__stories__/button.stories.mdx
index 5e6fa6236..ca77c4903 100644
--- a/platform/ui/src/components/Button/__stories__/button.stories.mdx
+++ b/platform/ui/src/components/Button/__stories__/button.stories.mdx
@@ -1,4 +1,4 @@
-import Button, {ButtonType, ButtonSize} from '../Button';
+import {Button, ButtonEnums} from '../../../components';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import {
createComponentTemplate,
@@ -45,8 +45,8 @@ There can be different types of buttons: `primary`, and `secondary`.
@@ -59,8 +59,8 @@ to the button's height.
@@ -71,7 +71,7 @@ You can mix different props together to create a button.