diff --git a/cypress/integration/FileInputField/has_default_button_label.feature b/cypress/integration/FileInputField/has_default_button_label.feature
new file mode 100644
index 0000000000..ffcd460936
--- /dev/null
+++ b/cypress/integration/FileInputField/has_default_button_label.feature
@@ -0,0 +1,5 @@
+Feature: Button label for the FileInputField
+
+ Scenario: Rendering a FileInputField
+ Given a default FileInputField is rendered
+ Then the default button label is visible
diff --git a/cypress/integration/FileInputField/has_default_button_label/index.js b/cypress/integration/FileInputField/has_default_button_label/index.js
new file mode 100644
index 0000000000..74f089078c
--- /dev/null
+++ b/cypress/integration/FileInputField/has_default_button_label/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a default FileInputField is rendered', () => {
+ cy.visitStory('FileInputField', 'Default')
+})
+
+Then('the default button label is visible', () => {
+ cy.contains('Upload a file').should('be.visible')
+})
diff --git a/cypress/integration/FileInputField/has_default_placeholder.feature b/cypress/integration/FileInputField/has_default_placeholder.feature
new file mode 100644
index 0000000000..196dda7627
--- /dev/null
+++ b/cypress/integration/FileInputField/has_default_placeholder.feature
@@ -0,0 +1,5 @@
+Feature: Placeholder for the FileInputField
+
+ Scenario: Rendering a FileInputField
+ Given a default FileInputField is rendered
+ Then the default placeholder is visible
diff --git a/cypress/integration/FileInputField/has_default_placeholder/index.js b/cypress/integration/FileInputField/has_default_placeholder/index.js
new file mode 100644
index 0000000000..479933862c
--- /dev/null
+++ b/cypress/integration/FileInputField/has_default_placeholder/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a default FileInputField is rendered', () => {
+ cy.visitStory('FileInputField', 'Default')
+})
+
+Then('the default placeholder is visible', () => {
+ cy.contains('No file uploaded yet').should('be.visible')
+})
diff --git a/cypress/integration/FileInputFieldWithList/has_default_button_label.feature b/cypress/integration/FileInputFieldWithList/has_default_button_label.feature
new file mode 100644
index 0000000000..30be53a624
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_button_label.feature
@@ -0,0 +1,5 @@
+Feature: Button label for the FileInputFieldWithList
+
+ Scenario: Rendering a FileInputFieldWithList
+ Given a default FileInputFieldWithList is rendered
+ Then the default button label is visible
diff --git a/cypress/integration/FileInputFieldWithList/has_default_button_label/index.js b/cypress/integration/FileInputFieldWithList/has_default_button_label/index.js
new file mode 100644
index 0000000000..43fbbcaaa7
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_button_label/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a default FileInputFieldWithList is rendered', () => {
+ cy.visitStory('FileInputFieldWithList', 'With default texts')
+})
+
+Then('the default button label is visible', () => {
+ cy.contains('Upload a file').should('be.visible')
+})
diff --git a/cypress/integration/FileInputFieldWithList/has_default_placeholder.feature b/cypress/integration/FileInputFieldWithList/has_default_placeholder.feature
new file mode 100644
index 0000000000..3cb31230f2
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_placeholder.feature
@@ -0,0 +1,5 @@
+Feature: Placeholder for the FileInputFieldWithList
+
+ Scenario: Rendering a FileInputFieldWithList
+ Given a default FileInputFieldWithList is rendered
+ Then the default placeholder is visible
diff --git a/cypress/integration/FileInputFieldWithList/has_default_placeholder/index.js b/cypress/integration/FileInputFieldWithList/has_default_placeholder/index.js
new file mode 100644
index 0000000000..d66afddd94
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_placeholder/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a default FileInputFieldWithList is rendered', () => {
+ cy.visitStory('FileInputFieldWithList', 'With default texts')
+})
+
+Then('the default placeholder is visible', () => {
+ cy.contains('No file uploaded yet').should('be.visible')
+})
diff --git a/cypress/integration/FileInputFieldWithList/has_default_remove_text.feature b/cypress/integration/FileInputFieldWithList/has_default_remove_text.feature
new file mode 100644
index 0000000000..231627c5ff
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_remove_text.feature
@@ -0,0 +1,5 @@
+Feature: Placeholder for the FileInputFieldWithList
+
+ Scenario: Rendering a FileInputFieldWithList
+ Given a default FileInputFieldWithList is rendered
+ Then the default remove text is visible
diff --git a/cypress/integration/FileInputFieldWithList/has_default_remove_text/index.js b/cypress/integration/FileInputFieldWithList/has_default_remove_text/index.js
new file mode 100644
index 0000000000..d4ec0da90e
--- /dev/null
+++ b/cypress/integration/FileInputFieldWithList/has_default_remove_text/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a default FileInputFieldWithList is rendered', () => {
+ cy.visitStory('FileInputFieldWithList', 'With file and default texts')
+})
+
+Then('the default remove text is visible', () => {
+ cy.contains('Remove').should('be.visible')
+})
diff --git a/cypress/integration/MultiSelectField/has_default_clear_text.feature b/cypress/integration/MultiSelectField/has_default_clear_text.feature
new file mode 100644
index 0000000000..368887c0e4
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_clear_text.feature
@@ -0,0 +1,5 @@
+Feature: Clear text for the MultiSelectField
+
+ Scenario: Rendering a clearable MultiSelectField
+ Given a clearable MultiSelectField with selected option is rendered
+ Then the clear text is visible
diff --git a/cypress/integration/MultiSelectField/has_default_clear_text/index.js b/cypress/integration/MultiSelectField/has_default_clear_text/index.js
new file mode 100644
index 0000000000..c166b76ca9
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_clear_text/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a clearable MultiSelectField with selected option is rendered', () => {
+ cy.visitStory('MultiSelectField', 'With clearable and selected option')
+})
+
+Then('the clear text is visible', () => {
+ cy.contains('Clear').should('be.visible')
+})
diff --git a/cypress/integration/MultiSelectField/has_default_empty_text.feature b/cypress/integration/MultiSelectField/has_default_empty_text.feature
new file mode 100644
index 0000000000..9fc50b7636
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_empty_text.feature
@@ -0,0 +1,6 @@
+Feature: Empty text for the MultiSelectField
+
+ Scenario: Rendering an empty MultiSelectField
+ Given an empty MultiSelectField is rendered
+ When the Select is opened
+ Then the empty text is visible
diff --git a/cypress/integration/MultiSelectField/has_default_empty_text/index.js b/cypress/integration/MultiSelectField/has_default_empty_text/index.js
new file mode 100644
index 0000000000..6303e807a2
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_empty_text/index.js
@@ -0,0 +1,13 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('an empty MultiSelectField is rendered', () => {
+ cy.visitStory('MultiSelectField', 'Without options')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the empty text is visible', () => {
+ cy.contains('No data found').should('be.visible')
+})
diff --git a/cypress/integration/MultiSelectField/has_default_filter_nomatch_text.feature b/cypress/integration/MultiSelectField/has_default_filter_nomatch_text.feature
new file mode 100644
index 0000000000..935d0a3ded
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_filter_nomatch_text.feature
@@ -0,0 +1,7 @@
+Feature: Nomatchtext for the MultiSelectField
+
+ Scenario: Rendering a filterable MultiSelectField
+ Given a filterable MultiSelectField is rendered
+ When the Select is opened
+ And a filter that does not match any options is entered
+ Then the no match text is visible
diff --git a/cypress/integration/MultiSelectField/has_default_filter_nomatch_text/index.js b/cypress/integration/MultiSelectField/has_default_filter_nomatch_text/index.js
new file mode 100644
index 0000000000..b1bfcef13b
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_filter_nomatch_text/index.js
@@ -0,0 +1,17 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a filterable MultiSelectField is rendered', () => {
+ cy.visitStory('MultiSelectField', 'With filterable')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+When('a filter that does not match any options is entered', () => {
+ cy.focused().type('Two')
+})
+
+Then('the no match text is visible', () => {
+ cy.contains('No options found').should('be.visible')
+})
diff --git a/cypress/integration/MultiSelectField/has_default_filter_placeholder.feature b/cypress/integration/MultiSelectField/has_default_filter_placeholder.feature
new file mode 100644
index 0000000000..50182d6be4
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_filter_placeholder.feature
@@ -0,0 +1,6 @@
+Feature: Filter placeholder for the MultiSelectField
+
+ Scenario: Rendering a filterable MultiSelectField
+ Given a filterable MultiSelectField is rendered
+ When the Select is opened
+ Then the filter placeholder exists
diff --git a/cypress/integration/MultiSelectField/has_default_filter_placeholder/index.js b/cypress/integration/MultiSelectField/has_default_filter_placeholder/index.js
new file mode 100644
index 0000000000..2fd35fba80
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_filter_placeholder/index.js
@@ -0,0 +1,15 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a filterable MultiSelectField is rendered', () => {
+ cy.visitStory('MultiSelectField', 'With filterable')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the filter placeholder exists', () => {
+ cy.get(
+ '[data-test="dhis2-uicore-multiselect-filterinput"] [placeholder="Type to filter options"]'
+ ).should('exist')
+})
diff --git a/cypress/integration/MultiSelectField/has_default_loading_text.feature b/cypress/integration/MultiSelectField/has_default_loading_text.feature
new file mode 100644
index 0000000000..a6c8aa4c00
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_loading_text.feature
@@ -0,0 +1,6 @@
+Feature: Filter placeholder for the MultiSelectField
+
+ Scenario: Rendering a filterable MultiSelectField
+ Given a loading MultiSelectField is rendered
+ When the Select is opened
+ Then the loading text is visible
diff --git a/cypress/integration/MultiSelectField/has_default_loading_text/index.js b/cypress/integration/MultiSelectField/has_default_loading_text/index.js
new file mode 100644
index 0000000000..79b32c0db6
--- /dev/null
+++ b/cypress/integration/MultiSelectField/has_default_loading_text/index.js
@@ -0,0 +1,13 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a loading MultiSelectField is rendered', () => {
+ cy.visitStory('MultiSelectField', 'With loading')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the loading text is visible', () => {
+ cy.contains('Loading options').should('be.visible')
+})
diff --git a/cypress/integration/SingleSelectField/has_default_clear_text.feature b/cypress/integration/SingleSelectField/has_default_clear_text.feature
new file mode 100644
index 0000000000..30518cc930
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_clear_text.feature
@@ -0,0 +1,5 @@
+Feature: Clear text for the SingleSelectField
+
+ Scenario: Rendering a clearable SingleSelectField
+ Given a clearable SingleSelectField with selected option is rendered
+ Then the clear text is visible
diff --git a/cypress/integration/SingleSelectField/has_default_clear_text/index.js b/cypress/integration/SingleSelectField/has_default_clear_text/index.js
new file mode 100644
index 0000000000..b5fb83fd06
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_clear_text/index.js
@@ -0,0 +1,9 @@
+import { Given, Then } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a clearable SingleSelectField with selected option is rendered', () => {
+ cy.visitStory('SingleSelectField', 'With clearable and selected option')
+})
+
+Then('the clear text is visible', () => {
+ cy.contains('Clear').should('be.visible')
+})
diff --git a/cypress/integration/SingleSelectField/has_default_empty_text.feature b/cypress/integration/SingleSelectField/has_default_empty_text.feature
new file mode 100644
index 0000000000..e6e623af61
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_empty_text.feature
@@ -0,0 +1,6 @@
+Feature: Empty text for the SingleSelectField
+
+ Scenario: Rendering an empty SingleSelectField
+ Given an empty SingleSelectField is rendered
+ When the Select is opened
+ Then the empty text is visible
diff --git a/cypress/integration/SingleSelectField/has_default_empty_text/index.js b/cypress/integration/SingleSelectField/has_default_empty_text/index.js
new file mode 100644
index 0000000000..1b56e9da9d
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_empty_text/index.js
@@ -0,0 +1,13 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('an empty SingleSelectField is rendered', () => {
+ cy.visitStory('SingleSelectField', 'Without options')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the empty text is visible', () => {
+ cy.contains('No data found').should('be.visible')
+})
diff --git a/cypress/integration/SingleSelectField/has_default_filter_nomatch_text.feature b/cypress/integration/SingleSelectField/has_default_filter_nomatch_text.feature
new file mode 100644
index 0000000000..1738fd45bd
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_filter_nomatch_text.feature
@@ -0,0 +1,7 @@
+Feature: Nomatchtext for the SingleSelectField
+
+ Scenario: Rendering a filterable SingleSelectField
+ Given a filterable SingleSelectField is rendered
+ When the Select is opened
+ And a filter that does not match any options is entered
+ Then the no match text is visible
diff --git a/cypress/integration/SingleSelectField/has_default_filter_nomatch_text/index.js b/cypress/integration/SingleSelectField/has_default_filter_nomatch_text/index.js
new file mode 100644
index 0000000000..177205040e
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_filter_nomatch_text/index.js
@@ -0,0 +1,17 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a filterable SingleSelectField is rendered', () => {
+ cy.visitStory('SingleSelectField', 'With filterable')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+When('a filter that does not match any options is entered', () => {
+ cy.focused().type('Two')
+})
+
+Then('the no match text is visible', () => {
+ cy.contains('No options found').should('be.visible')
+})
diff --git a/cypress/integration/SingleSelectField/has_default_filter_placeholder.feature b/cypress/integration/SingleSelectField/has_default_filter_placeholder.feature
new file mode 100644
index 0000000000..db13d45cc8
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_filter_placeholder.feature
@@ -0,0 +1,6 @@
+Feature: Filter placeholder for the SingleSelectField
+
+ Scenario: Rendering a filterable SingleSelectField
+ Given a filterable SingleSelectField is rendered
+ When the Select is opened
+ Then the filter placeholder exists
diff --git a/cypress/integration/SingleSelectField/has_default_filter_placeholder/index.js b/cypress/integration/SingleSelectField/has_default_filter_placeholder/index.js
new file mode 100644
index 0000000000..b843348fa3
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_filter_placeholder/index.js
@@ -0,0 +1,15 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a filterable SingleSelectField is rendered', () => {
+ cy.visitStory('SingleSelectField', 'With filterable')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the filter placeholder exists', () => {
+ cy.get(
+ '[data-test="dhis2-uicore-singleselect-filterinput"] [placeholder="Type to filter options"]'
+ ).should('exist')
+})
diff --git a/cypress/integration/SingleSelectField/has_default_loading_text.feature b/cypress/integration/SingleSelectField/has_default_loading_text.feature
new file mode 100644
index 0000000000..3a8167a3f0
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_loading_text.feature
@@ -0,0 +1,6 @@
+Feature: Filter placeholder for the SingleSelectField
+
+ Scenario: Rendering a filterable SingleSelectField
+ Given a loading SingleSelectField is rendered
+ When the Select is opened
+ Then the loading text is visible
diff --git a/cypress/integration/SingleSelectField/has_default_loading_text/index.js b/cypress/integration/SingleSelectField/has_default_loading_text/index.js
new file mode 100644
index 0000000000..faef236e2c
--- /dev/null
+++ b/cypress/integration/SingleSelectField/has_default_loading_text/index.js
@@ -0,0 +1,13 @@
+import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps'
+
+Given('a loading SingleSelectField is rendered', () => {
+ cy.visitStory('SingleSelectField', 'With loading')
+})
+
+When('the Select is opened', () => {
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
+})
+
+Then('the loading text is visible', () => {
+ cy.contains('Loading options').should('be.visible')
+})
diff --git a/packages/widgets/i18n/en.pot b/packages/widgets/i18n/en.pot
index f73e0f6e06..8f67f27cc7 100644
--- a/packages/widgets/i18n/en.pot
+++ b/packages/widgets/i18n/en.pot
@@ -5,8 +5,17 @@ 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: 2020-02-18T13:19:29.126Z\n"
-"PO-Revision-Date: 2020-02-18T13:19:29.126Z\n"
+"POT-Creation-Date: 2020-05-14T13:50:41.269Z\n"
+"PO-Revision-Date: 2020-05-14T13:50:41.269Z\n"
+
+msgid "Upload a file"
+msgstr ""
+
+msgid "No file uploaded yet"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
msgid "Search apps"
msgstr ""
@@ -29,5 +38,20 @@ msgstr ""
msgid "Logout"
msgstr ""
-msgid "Something went wrong with loading the children."
+msgid "Clear"
+msgstr ""
+
+msgid "No data found"
+msgstr ""
+
+msgid "Type to filter options"
+msgstr ""
+
+msgid "Loading options"
+msgstr ""
+
+msgid "No options found"
+msgstr ""
+
+msgid "Could not load children"
msgstr ""
diff --git a/packages/widgets/src/FileInputField/FileInputField.js b/packages/widgets/src/FileInputField/FileInputField.js
index 645405fc8a..cec5c40de6 100644
--- a/packages/widgets/src/FileInputField/FileInputField.js
+++ b/packages/widgets/src/FileInputField/FileInputField.js
@@ -1,5 +1,6 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
+import i18n from '@dhis2/d2-i18n'
import { sharedPropTypes } from '@dhis2/ui-constants'
@@ -10,6 +11,7 @@ import {
Field,
Label,
} from '@dhis2/ui-core'
+import translate from '../translate'
/**
* @module
@@ -63,7 +65,7 @@ const FileInputField = ({
- {!children && placeholder ? (
- {placeholder}
- ) : (
+ {children ? (
children
+ ) : (
+
+ {translate(placeholder)}
+
)}
@@ -93,6 +97,9 @@ const FileInputField = ({
FileInputField.defaultProps = {
accept: '*',
dataTest: 'dhis2-uiwidgets-fileinputfield',
+
+ buttonLabel: () => i18n.t('Upload a file'),
+ placeholder: () => i18n.t('No file uploaded yet'),
}
/**
@@ -104,9 +111,9 @@ FileInputField.defaultProps = {
* @prop {function} [onBlur]
* @prop {function} [onFocus]
* @prop {string} [label]
- * @prop {string} [buttonLabel]
+ * @prop {string|function} [buttonLabel]
* @prop {string} [className]
- * @prop {string} [placeholder]
+ * @prop {string|function} [placeholder]
* @prop {string} [tabIndex]
*
* @prop {boolean} [required]
@@ -130,7 +137,7 @@ FileInputField.defaultProps = {
*/
FileInputField.propTypes = {
accept: propTypes.string,
- buttonLabel: propTypes.string,
+ buttonLabel: propTypes.oneOfType([propTypes.string, propTypes.func]),
children: propTypes.node,
className: propTypes.string,
dataTest: propTypes.string,
@@ -142,7 +149,7 @@ FileInputField.propTypes = {
large: sharedPropTypes.sizePropType,
multiple: propTypes.bool,
name: propTypes.string,
- placeholder: propTypes.string,
+ placeholder: propTypes.oneOfType([propTypes.string, propTypes.func]),
required: propTypes.bool,
small: sharedPropTypes.sizePropType,
tabIndex: propTypes.string,
diff --git a/packages/widgets/src/FileInputField/FileInputField.stories.e2e.js b/packages/widgets/src/FileInputField/FileInputField.stories.e2e.js
index 63b06e2233..506fd05eb8 100644
--- a/packages/widgets/src/FileInputField/FileInputField.stories.e2e.js
+++ b/packages/widgets/src/FileInputField/FileInputField.stories.e2e.js
@@ -2,11 +2,15 @@ import React from 'react'
import { storiesOf } from '@storybook/react'
import { FileInputField } from './FileInputField.js'
-storiesOf('FileInputField', module).add('With label and required', () => (
-
-))
+storiesOf('FileInputField', module)
+ .add('With label and required', () => (
+
+ ))
+ .add('Default', () => (
+
+ ))
diff --git a/packages/widgets/src/FileInputField/FileInputField.stories.js b/packages/widgets/src/FileInputField/FileInputField.stories.js
index 6a250c6724..f3b6db1504 100644
--- a/packages/widgets/src/FileInputField/FileInputField.stories.js
+++ b/packages/widgets/src/FileInputField/FileInputField.stories.js
@@ -207,3 +207,6 @@ storiesOf('Components/Widgets/FileInputField', module)
name="upload"
/>
))
+ .add('Default: buttonLabel and placeholder', () => (
+
+ ))
diff --git a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.js b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.js
index f106886a17..5dc08e3990 100644
--- a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.js
+++ b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.js
@@ -1,10 +1,11 @@
import React, { Component } from 'react'
import propTypes from '@dhis2/prop-types'
import { sharedPropTypes } from '@dhis2/ui-constants'
+import i18n from '@dhis2/d2-i18n'
import { FileInputField } from '../FileInputField/FileInputField.js'
-
import { FileListItemWithRemove } from './FileListItemWithRemove.js'
+import translate from '../translate'
/**
* @module
@@ -92,7 +93,7 @@ class FileInputFieldWithList extends Component {
return (
= 1)}
@@ -106,7 +107,7 @@ class FileInputFieldWithList extends Component {
onBlur={onBlur}
onChange={this.handleChange}
onFocus={onFocus}
- placeholder={placeholder}
+ placeholder={translate(placeholder)}
required={required}
small={small}
tabIndex={tabIndex}
@@ -114,15 +115,16 @@ class FileInputFieldWithList extends Component {
validationText={validationText}
warning={warning}
>
- {files.map(file => (
-
- ))}
+ {files.length > 0 &&
+ files.map(file => (
+
+ ))}
)
}
@@ -131,22 +133,26 @@ class FileInputFieldWithList extends Component {
FileInputFieldWithList.defaultProps = {
dataTest: 'dhis2-uiwidgets-fileinputfieldwithlist',
files: [],
+
+ buttonLabel: () => i18n.t('Upload a file'),
+ placeholder: () => i18n.t('No file uploaded yet'),
+ removeText: () => i18n.t('Remove'),
}
/**
* @typedef {Object} PropTypes
* @static
*
- * @prop {string} [removeText]
+ * @prop {string|function} [removeText]
* @prop {function} onChange
* @prop {Array} [files=[]] - an array of File instances (NOTE: not a FileList instance)
* @prop {string} [name]
* @prop {function} [onBlur]
* @prop {function} [onFocus]
* @prop {string} [label]
- * @prop {string} [buttonLabel]
+ * @prop {string|function} [buttonLabel]
* @prop {string} [className]
- * @prop {string} [placeholder]
+ * @prop {string|function} [placeholder]
* @prop {string} [tabIndex]
*
* @prop {boolean} [required]
@@ -171,7 +177,7 @@ FileInputFieldWithList.defaultProps = {
FileInputFieldWithList.propTypes = {
onChange: propTypes.func.isRequired,
accept: propTypes.string,
- buttonLabel: propTypes.string,
+ buttonLabel: propTypes.oneOfType([propTypes.string, propTypes.func]),
className: propTypes.string,
dataTest: propTypes.string,
disabled: propTypes.bool,
@@ -183,8 +189,8 @@ FileInputFieldWithList.propTypes = {
large: sharedPropTypes.sizePropType,
multiple: propTypes.bool,
name: propTypes.string,
- placeholder: propTypes.string,
- removeText: propTypes.string,
+ placeholder: propTypes.oneOfType([propTypes.string, propTypes.func]),
+ removeText: propTypes.oneOfType([propTypes.string, propTypes.func]),
required: propTypes.bool,
small: sharedPropTypes.sizePropType,
tabIndex: propTypes.string,
diff --git a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.e2e.js b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.e2e.js
index 5767afba1d..54153d4636 100644
--- a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.e2e.js
+++ b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.e2e.js
@@ -62,3 +62,13 @@ storiesOf('FileInputFieldWithList', module)
onChange={onChange}
/>
))
+ .add('With file and default texts', () => (
+
+ ))
+ .add('With default texts', () => (
+
+ ))
diff --git a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.js b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.js
index 1eed3805c8..aa29bf4c1a 100644
--- a/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.js
+++ b/packages/widgets/src/FileInputFieldWithList/FileInputFieldWithList.stories.js
@@ -10,9 +10,8 @@ const onChange = ({ files }) => {
console.log('files: ', files)
}
-storiesOf('Components/Widgets/FileInputFieldWithList', module).add(
- 'Default',
- () => (
+storiesOf('Components/Widgets/FileInputFieldWithList', module)
+ .add('Default', () => (
- )
-)
+ ))
+ .add('Default: buttonLabel and removeText', () => (
+
+ ))
+ .add('Default: placeholder', () => (
+
+ ))
diff --git a/packages/widgets/src/MultiSelectField/MultiSelectField.js b/packages/widgets/src/MultiSelectField/MultiSelectField.js
index 322a82c160..ab7197216e 100644
--- a/packages/widgets/src/MultiSelectField/MultiSelectField.js
+++ b/packages/widgets/src/MultiSelectField/MultiSelectField.js
@@ -1,8 +1,10 @@
import propTypes from '@dhis2/prop-types'
import React from 'react'
+import i18n from '@dhis2/d2-i18n'
import { sharedPropTypes } from '@dhis2/ui-constants'
import { Field, Box, MultiSelect } from '@dhis2/ui-core'
+import translate from '../translate'
/**
* @module
@@ -80,14 +82,14 @@ class MultiSelectField extends React.Component {
valid={valid}
disabled={disabled}
clearable={clearable}
- clearText={clearText}
+ clearText={translate(clearText)}
filterable={filterable}
- filterPlaceholder={filterPlaceholder}
+ filterPlaceholder={translate(filterPlaceholder)}
placeholder={placeholder}
prefix={prefix}
- empty={empty}
- loadingText={loadingText}
- noMatchText={noMatchText}
+ empty={translate(empty)}
+ loadingText={translate(loadingText)}
+ noMatchText={translate(noMatchText)}
initialFocus={initialFocus}
dense={dense}
>
@@ -102,6 +104,12 @@ class MultiSelectField extends React.Component {
MultiSelectField.defaultProps = {
selected: [],
dataTest: 'dhis2-uiwidgets-multiselectfield',
+
+ clearText: () => i18n.t('Clear'),
+ empty: () => i18n.t('No data found'),
+ filterPlaceholder: () => i18n.t('Type to filter options'),
+ loadingText: () => i18n.t('Loading options'),
+ noMatchText: () => i18n.t('No options found'),
}
/**
@@ -126,16 +134,16 @@ MultiSelectField.defaultProps = {
* @prop {boolean} [initialFocus]
* @prop {string} [validationText]
* @prop {string} [helpText]
- * @prop {string} [clearText] - Only required if clearable is true
+ * @prop {string|function} [clearText]
* @prop {boolean} [clearable]
- * @prop {Node} [empty]
- * @prop {string} [filterPlaceholder]
+ * @prop {Node|function} [empty]
+ * @prop {string|function} [filterPlaceholder]
* @prop {boolean} [filterable]
- * @prop {string} [loadingText]
+ * @prop {string|function} [loadingText]
* @prop {string} [maxHeight]
* @prop {string} [inputMaxHeight]
* @prop {string} [inputWidth]
- * @prop {string} [noMatchText] - Only required if filterable is true
+ * @prop {string|function} [noMatchText]
* @prop {string} [placeholder]
* @prop {string} [prefix]
* @prop {string} [dataTest]
@@ -143,14 +151,14 @@ MultiSelectField.defaultProps = {
MultiSelectField.propTypes = {
children: propTypes.node,
className: propTypes.string,
- clearText: propTypes.requiredIf(props => props.clearable, propTypes.string),
+ clearText: propTypes.oneOfType([propTypes.string, propTypes.func]),
clearable: propTypes.bool,
dataTest: propTypes.string,
dense: propTypes.bool,
disabled: propTypes.bool,
- empty: propTypes.node,
+ empty: propTypes.oneOfType([propTypes.node, propTypes.func]),
error: sharedPropTypes.statusPropType,
- filterPlaceholder: propTypes.string,
+ filterPlaceholder: propTypes.oneOfType([propTypes.node, propTypes.func]),
filterable: propTypes.bool,
helpText: propTypes.string,
initialFocus: propTypes.bool,
@@ -158,12 +166,9 @@ MultiSelectField.propTypes = {
inputWidth: propTypes.string,
label: propTypes.string,
loading: propTypes.bool,
- loadingText: propTypes.string,
+ loadingText: propTypes.oneOfType([propTypes.string, propTypes.func]),
maxHeight: propTypes.string,
- noMatchText: propTypes.requiredIf(
- props => props.filterable,
- propTypes.string
- ),
+ noMatchText: propTypes.oneOfType([propTypes.string, propTypes.func]),
placeholder: propTypes.string,
prefix: propTypes.string,
required: propTypes.bool,
diff --git a/packages/widgets/src/MultiSelectField/MultiSelectField.stories.e2e.js b/packages/widgets/src/MultiSelectField/MultiSelectField.stories.e2e.js
index 3be1a95c07..6f7f013773 100644
--- a/packages/widgets/src/MultiSelectField/MultiSelectField.stories.e2e.js
+++ b/packages/widgets/src/MultiSelectField/MultiSelectField.stories.e2e.js
@@ -34,3 +34,15 @@ storiesOf('MultiSelectField', module)
))
+ .add('With clearable and selected option', () => (
+
+
+
+ ))
+ .add('With filterable', () => (
+
+
+
+ ))
+ .add('With loading', () => )
+ .add('Without options', () => )
diff --git a/packages/widgets/src/MultiSelectField/MultiSelectField.stories.js b/packages/widgets/src/MultiSelectField/MultiSelectField.stories.js
index c93b6a0863..29cca0b4c8 100644
--- a/packages/widgets/src/MultiSelectField/MultiSelectField.stories.js
+++ b/packages/widgets/src/MultiSelectField/MultiSelectField.stories.js
@@ -79,3 +79,17 @@ storiesOf('Components/Widgets/MultiSelectField', module)
{options}
))
+ .add('Default: clearText', () => (
+
+
+
+ ))
+ .add('Default: filterPlaceholder and noMatchText', () => (
+
+ ))
+ .add('Default: loadingText', () => )
+ .add('Default: empty', () => )
diff --git a/packages/widgets/src/SingleSelectField/SingleSelectField.js b/packages/widgets/src/SingleSelectField/SingleSelectField.js
index e6681fb66e..8e752b0525 100644
--- a/packages/widgets/src/SingleSelectField/SingleSelectField.js
+++ b/packages/widgets/src/SingleSelectField/SingleSelectField.js
@@ -1,8 +1,10 @@
import React from 'react'
+import i18n from '@dhis2/d2-i18n'
import propTypes from '@dhis2/prop-types'
import { Field, SingleSelect, Box } from '@dhis2/ui-core'
import { sharedPropTypes } from '@dhis2/ui-constants'
+import translate from '../translate'
/**
* @module
@@ -80,14 +82,14 @@ class SingleSelectField extends React.Component {
valid={valid}
disabled={disabled}
clearable={clearable}
- clearText={clearText}
+ clearText={translate(clearText)}
filterable={filterable}
- filterPlaceholder={filterPlaceholder}
+ filterPlaceholder={translate(filterPlaceholder)}
placeholder={placeholder}
prefix={prefix}
- empty={empty}
- loadingText={loadingText}
- noMatchText={noMatchText}
+ empty={translate(empty)}
+ loadingText={translate(loadingText)}
+ noMatchText={translate(noMatchText)}
initialFocus={initialFocus}
dense={dense}
>
@@ -100,8 +102,14 @@ class SingleSelectField extends React.Component {
}
SingleSelectField.defaultProps = {
- selected: '',
dataTest: 'dhis2-uiwidgets-singleselectfield',
+ selected: '',
+
+ clearText: () => i18n.t('Clear'),
+ empty: () => i18n.t('No data found'),
+ filterPlaceholder: () => i18n.t('Type to filter options'),
+ loadingText: () => i18n.t('Loading options'),
+ noMatchText: () => i18n.t('No options found'),
}
/**
@@ -126,16 +134,16 @@ SingleSelectField.defaultProps = {
* @prop {boolean} [initialFocus]
* @prop {string} [validationText]
* @prop {string} [helpText]
- * @prop {string} [clearText] - Only required if clearable is true
+ * @prop {string|function} [clearText]
* @prop {boolean} [clearable]
- * @prop {Node} [empty]
- * @prop {string} [filterPlaceholder]
+ * @prop {Node|function} [empty]
+ * @prop {string|function} [filterPlaceholder]
* @prop {boolean} [filterable]
- * @prop {string} [loadingText]
+ * @prop {string|function} [loadingText]
* @prop {string} [maxHeight]
* @prop {string} [inputMaxHeight]
* @prop {string} [inputWidth]
- * @prop {string} [noMatchText] - Only required if filterable is true
+ * @prop {string|function} [noMatchText]
* @prop {string} [placeholder]
* @prop {string} [prefix]
* @prop {string} [dataTest]
@@ -143,14 +151,14 @@ SingleSelectField.defaultProps = {
SingleSelectField.propTypes = {
children: propTypes.node,
className: propTypes.string,
- clearText: propTypes.requiredIf(props => props.clearable, propTypes.string),
+ clearText: propTypes.oneOfType([propTypes.string, propTypes.func]),
clearable: propTypes.bool,
dataTest: propTypes.string,
dense: propTypes.bool,
disabled: propTypes.bool,
- empty: propTypes.node,
+ empty: propTypes.oneOfType([propTypes.node, propTypes.func]),
error: sharedPropTypes.statusPropType,
- filterPlaceholder: propTypes.string,
+ filterPlaceholder: propTypes.oneOfType([propTypes.node, propTypes.func]),
filterable: propTypes.bool,
helpText: propTypes.string,
initialFocus: propTypes.bool,
@@ -158,12 +166,9 @@ SingleSelectField.propTypes = {
inputWidth: propTypes.string,
label: propTypes.string,
loading: propTypes.bool,
- loadingText: propTypes.string,
+ loadingText: propTypes.oneOfType([propTypes.string, propTypes.func]),
maxHeight: propTypes.string,
- noMatchText: propTypes.requiredIf(
- props => props.filterable,
- propTypes.string
- ),
+ noMatchText: propTypes.oneOfType([propTypes.string, propTypes.func]),
placeholder: propTypes.string,
prefix: propTypes.string,
required: propTypes.bool,
diff --git a/packages/widgets/src/SingleSelectField/SingleSelectField.stories.e2e.js b/packages/widgets/src/SingleSelectField/SingleSelectField.stories.e2e.js
index 9513ecaab6..d33814cb2a 100644
--- a/packages/widgets/src/SingleSelectField/SingleSelectField.stories.e2e.js
+++ b/packages/widgets/src/SingleSelectField/SingleSelectField.stories.e2e.js
@@ -34,3 +34,15 @@ storiesOf('SingleSelectField', module)
))
+ .add('With clearable and selected option', () => (
+
+
+
+ ))
+ .add('With filterable', () => (
+
+
+
+ ))
+ .add('With loading', () => )
+ .add('Without options', () => )
diff --git a/packages/widgets/src/SingleSelectField/SingleSelectField.stories.js b/packages/widgets/src/SingleSelectField/SingleSelectField.stories.js
index 1cd09885ec..fc1b73b7c8 100644
--- a/packages/widgets/src/SingleSelectField/SingleSelectField.stories.js
+++ b/packages/widgets/src/SingleSelectField/SingleSelectField.stories.js
@@ -78,3 +78,17 @@ storiesOf('Components/Widgets/SingleSelectField', module)
{options}
))
+ .add('Default: clearText', () => (
+
+
+
+ ))
+ .add('Default: filterPlaceholder and noMatchText', () => (
+
+ ))
+ .add('Default: loadingText', () => )
+ .add('Default: empty', () => )
diff --git a/packages/widgets/src/translate/__tests__/index.test.js b/packages/widgets/src/translate/__tests__/index.test.js
new file mode 100644
index 0000000000..a58a8cb385
--- /dev/null
+++ b/packages/widgets/src/translate/__tests__/index.test.js
@@ -0,0 +1,17 @@
+import translate from '../index.js'
+
+describe('translate', () => {
+ it('should call prop and return the result if it is a function', () => {
+ const spy = jest.fn(() => 'translated string')
+ const result = translate(spy)
+
+ expect(spy).toHaveBeenCalled()
+ expect(result).toBe('translated string')
+ })
+
+ it('should return prop as is if it is not a function', () => {
+ const result = translate('just a string')
+
+ expect(result).toBe('just a string')
+ })
+})
diff --git a/packages/widgets/src/translate/index.js b/packages/widgets/src/translate/index.js
new file mode 100644
index 0000000000..fa5b9a46e7
--- /dev/null
+++ b/packages/widgets/src/translate/index.js
@@ -0,0 +1,19 @@
+/*
+ * This allows us to delay calling i18n.t until after the
+ * language has been set. Otherwise the translations could
+ * potentially use the wrong language.
+ *
+ * It checks if the passed prop is a function. If so,
+ * it'll execute it. Since this should only be called during
+ * render, we can be certain that i18n will have initialized.
+ */
+
+const translate = prop => {
+ if (typeof prop === 'function') {
+ return prop()
+ }
+
+ return prop
+}
+
+export default translate