├── .babelrc
├── .eslintrc
├── .gitignore
├── .sass-lint.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
└── D3.md
├── gulpfile.js
├── karma.conf.js
├── package.json
├── services.js
├── src
├── framework
│ ├── _index.scss
│ ├── accordion
│ │ ├── Accordion.jsx
│ │ ├── Accordion.spec.jsx
│ │ ├── _accordion.scss
│ │ └── _index.scss
│ ├── accordionItem
│ │ ├── AccordionItem.jsx
│ │ ├── AccordionItem.spec.jsx
│ │ ├── _accordionItem.scss
│ │ └── _index.scss
│ ├── addOnControl
│ │ ├── AddOnControl.jsx
│ │ ├── AddOnControl.spec.jsx
│ │ ├── _addOnControl.scss
│ │ ├── _index.scss
│ │ ├── dropdown
│ │ │ ├── AddOnDropdown.jsx
│ │ │ ├── AddOnDropdown.spec.jsx
│ │ │ ├── AddOnDropdownOption.jsx
│ │ │ ├── AddOnDropdownOption.spec.jsx
│ │ │ ├── _addOnDropdown.scss
│ │ │ └── _addOnDropdownOption.scss
│ │ └── label
│ │ │ ├── AddOnLabel.jsx
│ │ │ ├── AddOnLabel.spec.jsx
│ │ │ └── _addOnLabel.scss
│ ├── alert
│ │ ├── Alert.jsx
│ │ ├── Alert.spec.jsx
│ │ ├── _alert.scss
│ │ └── _index.scss
│ ├── appHeader
│ │ ├── AppHeader.jsx
│ │ ├── AppHeader.spec.jsx
│ │ ├── _appHeader.scss
│ │ ├── _index.scss
│ │ ├── account
│ │ │ ├── AccountNav.jsx
│ │ │ ├── AccountNav.spec.jsx
│ │ │ ├── AccountPicture.jsx
│ │ │ ├── AccountPicture.spec.jsx
│ │ │ ├── _accountNav.scss
│ │ │ └── _accountPicture.scss
│ │ ├── button
│ │ │ ├── AppHeaderButton.jsx
│ │ │ ├── AppHeaderButton.spec.jsx
│ │ │ └── _appHeaderButton.scss
│ │ ├── divider
│ │ │ ├── AppHeaderDivider.jsx
│ │ │ ├── AppHeaderDivider.spec.jsx
│ │ │ └── _appHeaderDivider.scss
│ │ ├── logo
│ │ │ ├── AppLogo.jsx
│ │ │ ├── AppLogo.spec.jsx
│ │ │ └── _appLogo.scss
│ │ ├── nav
│ │ │ ├── AppNav.jsx
│ │ │ ├── AppNav.spec.jsx
│ │ │ └── _appNav.scss
│ │ └── title
│ │ │ ├── AppTitle.jsx
│ │ │ ├── AppTitle.spec.jsx
│ │ │ ├── AppTitleContainer.jsx
│ │ │ ├── AppTitleContainer.spec.jsx
│ │ │ ├── _appTitle.scss
│ │ │ └── _appTitleContainer.scss
│ ├── base
│ │ ├── _index.scss
│ │ └── dropdown
│ │ │ ├── BaseDropdown.jsx
│ │ │ ├── BaseDropdown.spec.jsx
│ │ │ ├── BaseDropdownOption.jsx
│ │ │ ├── BaseDropdownOption.spec.jsx
│ │ │ └── _index.scss
│ ├── body
│ │ ├── Body.jsx
│ │ ├── Body.spec.jsx
│ │ ├── BodyMaxWidthLayout.jsx
│ │ ├── BodyMaxWidthLayout.spec.jsx
│ │ ├── BodyPanel.jsx
│ │ ├── BodyPanel.spec.jsx
│ │ ├── BodyPanelItem.jsx
│ │ ├── BodyPanelItem.spec.jsx
│ │ ├── _body.scss
│ │ ├── _bodyMaxWidthLayout.scss
│ │ ├── _bodyPanel.scss
│ │ ├── _bodyPanelItem.scss
│ │ └── _index.scss
│ ├── box
│ │ ├── Box.jsx
│ │ ├── Box.spec.jsx
│ │ ├── _box.scss
│ │ └── _index.scss
│ ├── button
│ │ ├── AlertButton.jsx
│ │ ├── AlertButton.spec.jsx
│ │ ├── BasicButton.jsx
│ │ ├── BasicButton.spec.jsx
│ │ ├── Button.jsx
│ │ ├── Button.spec.jsx
│ │ ├── CallOutButton.jsx
│ │ ├── CallOutButton.spec.jsx
│ │ ├── GroupedButton.jsx
│ │ ├── GroupedButton.spec.jsx
│ │ ├── HollowButton.jsx
│ │ ├── HollowButton.spec.jsx
│ │ ├── PrimaryButton.jsx
│ │ ├── PrimaryButton.spec.jsx
│ │ ├── _button.scss
│ │ ├── _index.scss
│ │ └── index.jsx
│ ├── buttonGroup
│ │ ├── ButtonGroup.jsx
│ │ ├── ButtonGroup.spec.jsx
│ │ ├── _buttonGroup.scss
│ │ └── _index.scss
│ ├── card
│ │ ├── Card.jsx
│ │ ├── Card.spec.jsx
│ │ ├── _card.scss
│ │ └── _index.scss
│ ├── cardHolder
│ │ ├── CardHolder.jsx
│ │ ├── CardHolder.spec.jsx
│ │ ├── _cardHolder.scss
│ │ └── _index.scss
│ ├── changeLog
│ │ ├── ChangeLogLink.jsx
│ │ ├── ChangeLogLink.spec.jsx
│ │ ├── ChangeLogModal.jsx
│ │ ├── ChangeLogModal.spec.jsx
│ │ ├── ChangeLogService.js
│ │ ├── ChangeLogService.spec.js
│ │ ├── _changeLogLink.scss
│ │ ├── _changeLogModal.scss
│ │ ├── _index.scss
│ │ ├── helpers.js
│ │ └── helpers.spec.js
│ ├── chart
│ │ ├── Chart.jsx
│ │ ├── Chart.spec.jsx
│ │ ├── LineChart.jsx
│ │ ├── LineChart.spec.jsx
│ │ ├── _chart.scss
│ │ ├── _index.scss
│ │ ├── _lineChart.scss
│ │ └── chartDot
│ │ │ ├── ChartDot.jsx
│ │ │ ├── ChartDot.spec.jsx
│ │ │ └── _chartDot.scss
│ ├── checkBox
│ │ ├── CheckBox.jsx
│ │ ├── CheckBox.spec.jsx
│ │ ├── _checkBox.scss
│ │ └── _index.scss
│ ├── columnLayout
│ │ ├── Column.jsx
│ │ ├── Column.spec.jsx
│ │ ├── ColumnLayout.jsx
│ │ ├── ColumnLayout.spec.jsx
│ │ ├── _columnLayout.scss
│ │ └── _index.scss
│ ├── dropdown
│ │ ├── Dropdown.jsx
│ │ ├── Dropdown.spec.jsx
│ │ ├── DropdownGroup.jsx
│ │ ├── DropdownGroup.spec.jsx
│ │ ├── DropdownOption.jsx
│ │ ├── DropdownOption.spec.jsx
│ │ ├── _dropdown.scss
│ │ ├── _dropdownOption.scss
│ │ ├── _index.scss
│ │ └── dropdownDot
│ │ │ ├── DropdownDot.jsx
│ │ │ ├── DropdownDot.spec.jsx
│ │ │ └── _index.scss
│ ├── fieldMessage
│ │ ├── FieldMessage.jsx
│ │ ├── FieldMessage.spec.jsx
│ │ └── _index.scss
│ ├── filterControl
│ │ ├── FilterControl.jsx
│ │ ├── FilterControl.spec.jsx
│ │ ├── FilterItem.jsx
│ │ ├── FilterItem.spec.jsx
│ │ ├── _filterControl.scss
│ │ ├── _filterItem.scss
│ │ ├── _index.scss
│ │ ├── filterDropdown
│ │ │ ├── FilterDropdown.jsx
│ │ │ ├── FilterDropdown.spec.jsx
│ │ │ ├── FilterDropdownButton.jsx
│ │ │ ├── FilterDropdownButton.spec.jsx
│ │ │ ├── FilterFormDropdown.jsx
│ │ │ ├── FilterFormDropdown.spec.jsx
│ │ │ ├── _filterDropdown.scss
│ │ │ ├── _filterFormDropdown.scss
│ │ │ └── filterOptions
│ │ │ │ ├── FilterOptionList.jsx
│ │ │ │ ├── FilterOptionList.spec.jsx
│ │ │ │ └── _filterOptionList.scss
│ │ └── forms
│ │ │ ├── DateRangeFilterForm.jsx
│ │ │ ├── DateRangeFilterForm.spec.jsx
│ │ │ ├── FilterForm.jsx
│ │ │ ├── FilterForm.spec.jsx
│ │ │ ├── InputFilterForm.jsx
│ │ │ ├── InputFilterForm.spec.jsx
│ │ │ ├── MixedTypeValueFilterForm.jsx
│ │ │ ├── MixedTypeValueFilterForm.spec.jsx
│ │ │ ├── MultipleSelectFilterForm.jsx
│ │ │ ├── MultipleSelectFilterForm.spec.jsx
│ │ │ ├── SearchableMultipleSelectFilterForm.jsx
│ │ │ ├── SearchableMultipleSelectFilterForm.spec.jsx
│ │ │ ├── _filterForm.scss
│ │ │ ├── _inputFilterForm.scss
│ │ │ ├── _multipleSelectFilterForm.scss
│ │ │ └── _searchableMultiSelectFilterForm.scss
│ ├── form
│ │ ├── Form.jsx
│ │ ├── Form.spec.jsx
│ │ ├── FormPanel.jsx
│ │ ├── FormPanel.spec.jsx
│ │ ├── _formPanel.scss
│ │ ├── _index.scss
│ │ └── footer
│ │ │ ├── FormFooter.jsx
│ │ │ ├── FormFooter.spec.jsx
│ │ │ └── _formFooter.scss
│ ├── framework.js
│ ├── framework.spec.js
│ ├── global
│ │ ├── _blur.scss
│ │ ├── _body.scss
│ │ ├── _button.scss
│ │ ├── _colors.scss
│ │ ├── _cssIcons.scss
│ │ ├── _fonts.scss
│ │ ├── _footer.scss
│ │ ├── _form.scss
│ │ ├── _index.scss
│ │ ├── _label.scss
│ │ ├── _list.scss
│ │ ├── _logo.scss
│ │ ├── _misc.scss
│ │ ├── _modal.scss
│ │ └── _reset.scss
│ ├── glossary
│ │ ├── GlossaryItem.jsx
│ │ ├── GlossaryProvider.jsx
│ │ ├── GlossaryTooltip.jsx
│ │ └── _index.scss
│ ├── grid
│ │ ├── Grid.jsx
│ │ ├── Grid.spec.jsx
│ │ ├── _index.scss
│ │ ├── body
│ │ │ ├── GridBody.jsx
│ │ │ ├── GridBody.spec.jsx
│ │ │ ├── GridBodyCell.jsx
│ │ │ ├── GridBodyCell.spec.jsx
│ │ │ ├── GridBodyEditableCell.jsx
│ │ │ ├── GridBodyEditableCell.spec.jsx
│ │ │ ├── GridFakeRow.jsx
│ │ │ ├── GridFakeRow.spec.jsx
│ │ │ ├── GridRow.jsx
│ │ │ ├── GridRow.spec.jsx
│ │ │ ├── _gridBody.scss
│ │ │ ├── _gridBodyCell.scss
│ │ │ ├── _gridRow.scss
│ │ │ └── _index.scss
│ │ ├── controls
│ │ │ ├── GridControls.jsx
│ │ │ ├── GridControls.spec.jsx
│ │ │ ├── _gridControls.scss
│ │ │ ├── _index.scss
│ │ │ └── batch
│ │ │ │ ├── _batch.scss
│ │ │ │ └── _index.scss
│ │ ├── empty
│ │ │ ├── GridEmptyRow.jsx
│ │ │ ├── GridEmptyRow.spec.jsx
│ │ │ ├── _gridEmptyRow.scss
│ │ │ └── _index.scss
│ │ ├── footer
│ │ │ ├── GridFooter.jsx
│ │ │ ├── GridFooter.spec.jsx
│ │ │ ├── GridFooterCell.jsx
│ │ │ ├── GridFooterCell.spec.jsx
│ │ │ ├── _gridFooter.scss
│ │ │ └── _index.scss
│ │ ├── header
│ │ │ ├── GridHeader.jsx
│ │ │ ├── GridHeader.spec.jsx
│ │ │ ├── GridHeaderCell.jsx
│ │ │ ├── GridHeaderCell.spec.jsx
│ │ │ ├── GridHeaderSortableCell.jsx
│ │ │ ├── GridHeaderSortableCell.spec.jsx
│ │ │ ├── _gridHeader.scss
│ │ │ └── _index.scss
│ │ ├── icon
│ │ │ ├── GridIcon.jsx
│ │ │ ├── GridIcon.spec.jsx
│ │ │ └── _index.scss
│ │ ├── loading
│ │ │ ├── GridLoadingRow.jsx
│ │ │ ├── GridLoadingRow.spec.jsx
│ │ │ ├── _gridLoadingRow.scss
│ │ │ └── _index.scss
│ │ └── stickyGrid
│ │ │ ├── StickyGrid.jsx
│ │ │ ├── StickyGrid.spec.jsx
│ │ │ ├── _index.scss
│ │ │ └── _stickyGrid.scss
│ ├── horizontalLine
│ │ ├── HorizontalLine.jsx
│ │ ├── HorizontalLine.spec.jsx
│ │ ├── _horizontalLine.scss
│ │ └── _index.scss
│ ├── imageUpload
│ │ ├── ImagePreview.jsx
│ │ ├── ImagePreview.spec.jsx
│ │ ├── ImageUpload.jsx
│ │ ├── ImageUpload.spec.jsx
│ │ ├── _imageUpload.scss
│ │ └── _index.scss
│ ├── kpi
│ │ ├── Kpi.jsx
│ │ ├── Kpi.spec.jsx
│ │ ├── KpiNegative.jsx
│ │ ├── KpiNegative.spec.jsx
│ │ ├── KpiPositive.jsx
│ │ ├── KpiPositive.spec.jsx
│ │ ├── _index.scss
│ │ └── index.jsx
│ ├── label
│ │ ├── Label.jsx
│ │ ├── Label.spec.jsx
│ │ ├── SubLabel.jsx
│ │ ├── SubLabel.spec.jsx
│ │ ├── _index.scss
│ │ ├── _label.scss
│ │ └── _subLabel.scss
│ ├── labeledField
│ │ ├── LabeledField.jsx
│ │ ├── LabeledField.spec.jsx
│ │ ├── _index.scss
│ │ └── _labeledField.scss
│ ├── leftFixedLayout
│ │ ├── LeftFixedLayout.jsx
│ │ ├── LeftFixedLayout.spec.jsx
│ │ ├── _index.scss
│ │ └── _leftFixedLayout.scss
│ ├── link
│ │ ├── Link.jsx
│ │ ├── Link.spec.jsx
│ │ ├── _index.scss
│ │ └── _link.scss
│ ├── menu
│ │ ├── Menu.jsx
│ │ ├── Menu.spec.jsx
│ │ ├── MenuItem.jsx
│ │ ├── MenuItem.spec.jsx
│ │ ├── _index.scss
│ │ └── _menuItem.scss
│ ├── modal
│ │ ├── Modal.jsx
│ │ ├── Modal.spec.jsx
│ │ ├── ModalBody.jsx
│ │ ├── ModalBody.spec.jsx
│ │ ├── _index.scss
│ │ ├── _modal.scss
│ │ ├── confirmation
│ │ │ ├── ModalConfirmationBody.jsx
│ │ │ ├── ModalConfirmationBody.spec.jsx
│ │ │ ├── ModalConfirmationFooter.jsx
│ │ │ ├── ModalConfirmationFooter.spec.jsx
│ │ │ └── _modalConfirmation.scss
│ │ ├── footer
│ │ │ ├── ModalFooter.jsx
│ │ │ ├── ModalFooter.spec.jsx
│ │ │ └── _modalFooter.scss
│ │ ├── header
│ │ │ ├── ModalCloseButton.jsx
│ │ │ ├── ModalCloseButton.spec.jsx
│ │ │ ├── ModalHeader.jsx
│ │ │ ├── ModalHeader.spec.jsx
│ │ │ └── _modalHeader.scss
│ │ ├── overlay
│ │ │ ├── ModalOverlay.jsx
│ │ │ ├── ModalOverlay.spec.jsx
│ │ │ └── _modalOverlay.scss
│ │ └── stack
│ │ │ ├── ModalStack.jsx
│ │ │ ├── ModalStack.spec.jsx
│ │ │ └── _modalStack.scss
│ ├── organizationSwitcher
│ │ ├── OrganizationSwitcher.jsx
│ │ ├── OrganizationSwitcher.spec.jsx
│ │ ├── OrganizationSwitcherItem.jsx
│ │ ├── OrganizationSwitcherItem.spec.jsx
│ │ ├── _index.scss
│ │ ├── _organizationSwitcher.scss
│ │ └── _organizationSwitcherItem.scss
│ ├── pagination
│ │ ├── Pagination.jsx
│ │ ├── Pagination.spec.jsx
│ │ ├── _index.scss
│ │ └── _pagination.scss
│ ├── panel
│ │ ├── Panel.jsx
│ │ ├── Panel.spec.jsx
│ │ ├── PanelLayout.jsx
│ │ ├── PanelLayout.spec.jsx
│ │ ├── _index.scss
│ │ ├── _panel.scss
│ │ ├── _panelLayout.scss
│ │ └── progress
│ │ │ ├── PanelProgress.jsx
│ │ │ ├── PanelProgress.spec.jsx
│ │ │ └── _panelProgress.scss
│ ├── pickedList
│ │ ├── PickedList.jsx
│ │ ├── PickedList.spec.jsx
│ │ ├── PickedListItem.jsx
│ │ ├── PickedListItem.spec.jsx
│ │ ├── _index.scss
│ │ ├── _pickedList.scss
│ │ └── _pickedListItem.scss
│ ├── pickedSummary
│ │ ├── PickedSummary.jsx
│ │ ├── PickedSummary.spec.jsx
│ │ ├── _index.scss
│ │ └── _pickedSummary.scss
│ ├── progress
│ │ ├── Progress.jsx
│ │ ├── Progress.spec.jsx
│ │ ├── ProgressModal.jsx
│ │ ├── ProgressModal.spec.jsx
│ │ ├── ProgressSuccess.jsx
│ │ ├── ProgressSuccess.spec.jsx
│ │ ├── _index.scss
│ │ ├── _progress.scss
│ │ ├── _progressModal.scss
│ │ └── _progressSuccess.scss
│ ├── radioButtonItem
│ │ ├── RadioButtonItem.jsx
│ │ ├── RadioButtonItem.spec.jsx
│ │ ├── _index.scss
│ │ └── _radioButtonItem.scss
│ ├── radioButtons
│ │ ├── RadioButtons.jsx
│ │ └── RadioButtons.spec.jsx
│ ├── recycledList
│ │ ├── RecycledList.jsx
│ │ └── RecycledList.spec.jsx
│ ├── ribbon
│ │ ├── Ribbon.jsx
│ │ ├── Ribbon.spec.jsx
│ │ ├── _index.scss
│ │ └── _ribbon.scss
│ ├── searchBox
│ │ ├── SearchBox.jsx
│ │ ├── SearchBox.spec.jsx
│ │ ├── _index.scss
│ │ └── _searchBox.scss
│ ├── services.js
│ ├── services.spec.js
│ ├── services
│ │ ├── event
│ │ │ ├── EscapeKeyHandler.js
│ │ │ ├── EscapeKeyHandler.spec.js
│ │ │ ├── ThrottledEventDispatcher.js
│ │ │ ├── ThrottledEventDispatcher.spec.js
│ │ │ └── polyfillCustomEvent.js
│ │ ├── filter
│ │ │ ├── ComparisonTypes.js
│ │ │ ├── Filter.js
│ │ │ ├── Filter.spec.js
│ │ │ ├── FilterOption.js
│ │ │ ├── FilterOption.spec.js
│ │ │ ├── FilterableItems.js
│ │ │ ├── FilterableItems.spec.js
│ │ │ ├── MixedTypeValueFilter.js
│ │ │ ├── MixedTypeValueFilter.spec.js
│ │ │ ├── OneOfOption.js
│ │ │ └── OneOfOption.spec.js
│ │ ├── grid
│ │ │ └── GridStencil.js
│ │ ├── number
│ │ │ ├── Number.js
│ │ │ └── Number.spec.js
│ │ ├── object
│ │ │ ├── ObjectUtil.js
│ │ │ └── ObjectUtil.spec.js
│ │ ├── scroll
│ │ │ ├── ScrollPosition.js
│ │ │ └── ScrollPosition.spec.js
│ │ ├── sort
│ │ │ ├── SortState.js
│ │ │ ├── SortState.spec.js
│ │ │ ├── Sorter.js
│ │ │ └── Sorter.spec.js
│ │ ├── string
│ │ │ └── Entity.js
│ │ └── test
│ │ │ └── CommonAssertions.js
│ ├── statusDropdown
│ │ ├── StatusDropdown.jsx
│ │ ├── StatusDropdown.spec.jsx
│ │ ├── StatusDropdownOption.jsx
│ │ ├── StatusDropdownOption.spec.jsx
│ │ ├── _index.scss
│ │ ├── _statusDropdown.scss
│ │ ├── _statusDropdownOption.scss
│ │ └── statusDropdownOptionIcon
│ │ │ ├── StatusDropdownOptionIcon.jsx
│ │ │ ├── StatusDropdownOptionIcon.spec.jsx
│ │ │ └── _index.scss
│ ├── summaryControl
│ │ ├── SummaryControl.jsx
│ │ ├── SummaryControl.spec.jsx
│ │ ├── _index.scss
│ │ └── _summaryControl.scss
│ ├── text
│ │ ├── DescriptionText.jsx
│ │ ├── DescriptionText.spec.jsx
│ │ ├── Heading.jsx
│ │ ├── Heading.spec.jsx
│ │ ├── Text.jsx
│ │ ├── Text.spec.jsx
│ │ ├── _descriptionText.scss
│ │ ├── _heading.scss
│ │ ├── _index.scss
│ │ └── _text.scss
│ ├── textArea
│ │ ├── TextArea.jsx
│ │ ├── TextArea.spec.jsx
│ │ ├── _index.scss
│ │ └── _textArea.scss
│ ├── textInput
│ │ ├── TextInput.jsx
│ │ ├── TextInput.spec.jsx
│ │ ├── _index.scss
│ │ └── _textInput.scss
│ ├── titleBar
│ │ ├── TitleBar.jsx
│ │ ├── TitleBar.spec.jsx
│ │ ├── _index.scss
│ │ └── _titleBar.scss
│ ├── toggle
│ │ ├── Toggle.jsx
│ │ ├── Toggle.spec.jsx
│ │ ├── _index.scss
│ │ └── _toggle.scss
│ ├── tooltip
│ │ ├── Tooltip.jsx
│ │ ├── Tooltip.spec.jsx
│ │ ├── _index.scss
│ │ └── _tooltip.scss
│ ├── verticalLayout
│ │ ├── VerticalLayout.jsx
│ │ ├── VerticalLayout.spec.jsx
│ │ ├── _index.scss
│ │ └── _verticalLayout.scss
│ └── viewHeader
│ │ ├── ViewHeader.jsx
│ │ ├── ViewHeader.spec.jsx
│ │ ├── _index.scss
│ │ ├── _viewHeader.scss
│ │ ├── dateRange
│ │ ├── DateRange.jsx
│ │ └── _dateRange.scss
│ │ └── nav
│ │ ├── ViewHeaderNav.jsx
│ │ ├── ViewHeaderNav.spec.jsx
│ │ └── _viewHeaderNav.scss
└── guide
│ ├── components
│ ├── navigation
│ │ ├── NavItem.jsx
│ │ ├── NavTitle.jsx
│ │ ├── Navigation.jsx
│ │ └── _navigation.scss
│ ├── page
│ │ ├── Example.jsx
│ │ ├── Page.jsx
│ │ ├── SubTitle.jsx
│ │ ├── Text.jsx
│ │ └── Title.jsx
│ └── sourceCodeViewer
│ │ ├── SourceCodeViewer.jsx
│ │ └── _sourceCodeViewer.scss
│ ├── index.jade
│ ├── index.js
│ ├── index.scss
│ ├── services
│ ├── route
│ │ └── Route.js
│ └── string
│ │ └── Slug.js
│ ├── store
│ └── configureStore.js
│ └── views
│ ├── AppContainer.js
│ ├── AppView.jsx
│ ├── _app.scss
│ ├── accordion
│ └── AccordionExample.jsx
│ ├── addOnControl
│ └── AddOnControlExample.jsx
│ ├── alert
│ └── AlertExample.jsx
│ ├── appHeader
│ └── AppHeaderExample.jsx
│ ├── body
│ └── BodyExample.jsx
│ ├── box
│ └── BoxExample.jsx
│ ├── button
│ └── ButtonExample.jsx
│ ├── buttonGroup
│ └── ButtonGroupExample.jsx
│ ├── card
│ ├── CardExample.jsx
│ ├── _cardExample.scss
│ └── blueRibbon.png
│ ├── chart
│ ├── ChartExample.jsx
│ └── chartExampleData.js
│ ├── checkBox
│ └── CheckBoxExample.jsx
│ ├── columnLayout
│ └── ColumnLayoutExample.jsx
│ ├── dropdown
│ └── DropdownExample.jsx
│ ├── fieldMessage
│ └── FieldMessageExample.jsx
│ ├── form
│ └── FormExample.jsx
│ ├── grid
│ ├── GridExample.jsx
│ ├── createRows.js
│ └── gridExampleFilterOptions.js
│ ├── gridView
│ ├── GridExample.jsx
│ └── GridViewExample.jsx
│ ├── home
│ ├── HomeView.jsx
│ └── _homeView.scss
│ ├── horizontalLine
│ └── HorizontalLineExample.jsx
│ ├── htmlElements
│ └── HtmlElementsExample.jsx
│ ├── imageUpload
│ └── ImageUploadExample.jsx
│ ├── kpi
│ └── KpiExample.jsx
│ ├── label
│ └── LabelExample.jsx
│ ├── labeledField
│ └── LabeledFieldExample.jsx
│ ├── leftFixedLayout
│ └── LeftFixedLayoutExample.jsx
│ ├── link
│ └── LinkExample.jsx
│ ├── menu
│ └── MenuExample.jsx
│ ├── modal
│ └── ModalExample.jsx
│ ├── notFound
│ └── NotFoundView.jsx
│ ├── organizationSwitcher
│ └── OrganizationSwitcherExample.jsx
│ ├── pagination
│ └── PaginationExample.jsx
│ ├── panel
│ └── PanelExample.jsx
│ ├── pickedList
│ └── PickedListExample.jsx
│ ├── pickedSummary
│ └── PickedSummaryExample.jsx
│ ├── progress
│ └── ProgressExample.jsx
│ ├── radioButtons
│ └── RadioButtonsExample.jsx
│ ├── searchBox
│ └── SearchBoxExample.jsx
│ ├── statusDropdown
│ └── StatusDropdownExample.jsx
│ ├── summaryControl
│ └── SummaryControlExample.jsx
│ ├── text
│ └── TextExample.jsx
│ ├── textArea
│ └── TextAreaExample.jsx
│ ├── textInput
│ └── TextInputExample.jsx
│ ├── titleBar
│ └── TitleBarExample.jsx
│ ├── titledView
│ └── TitledViewExample.jsx
│ ├── toggle
│ └── ToggleExample.jsx
│ ├── tooltip
│ └── TooltipExample.jsx
│ ├── verticalLayout
│ └── VerticalLayoutExample.jsx
│ └── viewHeader
│ └── ViewHeaderExample.jsx
└── test
└── assets
└── img
└── testImage.png
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "developmentWithHmr": {
4 | "plugins": [
5 | "react-transform"
6 | ],
7 | "extra": {
8 | "react-transform": {
9 | "transforms": [{
10 | "transform": "react-transform-hmr",
11 | "imports": ["react"],
12 | "locals": ["module"]
13 | }]
14 | }
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "smaato",
3 | "env": {
4 | "browser": true,
5 | },
6 | "rules": {
7 | "class-methods-use-this": 0,
8 | "consistent-return": 0,
9 | "comma-dangle": [2, {
10 | "arrays": "always-multiline",
11 | "objects": "always-multiline",
12 | "imports": "always-multiline",
13 | "exports": "always-multiline",
14 | "functions": "ignore",
15 | }],
16 | "import/extensions": 0,
17 | "import/no-extraneous-dependencies": 0,
18 | "jsx-a11y/no-static-element-interactions": 0,
19 | "react/forbid-prop-types": 0,
20 | "react/jsx-filename-extension": 0,
21 | "react/no-array-index-key": 0,
22 | "react/no-string-refs": 0,
23 | "react/require-default-props": 0,
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .publish
2 | .sass-cache
3 | .DS_Store
4 | coverage
5 | node_modules
6 | dist
7 | reports
8 | *.log
9 | .idea/
10 | *.iml
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This project is intended for internal use as Smaato and for sharing some of our
4 | development process with the community, but not for production use by the
5 | public. As such, we would appreciate any Pull Requests regarding bug fixes,
6 | but we won't be able to accept any Pull Requests for additional features or
7 | improvements, since there's no guarantee they'll align with our goals as a
8 | company.
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License
3 |
4 | Copyright (c) 2015-2016 Smaato Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/services.js:
--------------------------------------------------------------------------------
1 |
2 | export * from './src/framework/services';
3 |
--------------------------------------------------------------------------------
/src/framework/accordion/Accordion.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import AccordionItem from './../accordionItem/AccordionItem.jsx';
6 |
7 | const Accordion = (props) => {
8 | const wrapperStyle = {
9 | width: props.width,
10 | };
11 |
12 | return (
13 |
14 | {props.children}
15 |
16 | );
17 | };
18 |
19 | Accordion.defaultProps = {
20 | width: '210px',
21 | };
22 |
23 | Accordion.propTypes = {
24 | children: PropTypes.arrayOf(AccordionItem),
25 | width: PropTypes.string,
26 | };
27 |
28 | export default Accordion;
29 |
--------------------------------------------------------------------------------
/src/framework/accordion/_accordion.scss:
--------------------------------------------------------------------------------
1 |
2 | .accordion {
3 | border: 1px solid #d4d8db;
4 | }
5 |
--------------------------------------------------------------------------------
/src/framework/accordion/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'accordion';
3 |
--------------------------------------------------------------------------------
/src/framework/accordionItem/_accordionItem.scss:
--------------------------------------------------------------------------------
1 |
2 | .accordion__item {
3 | display: flex;
4 | flex-direction: column;
5 | height: 100%;
6 | overflow: hidden;
7 |
8 | &:not(:last-child) .accordion__item__title,
9 | &:not(:last-child) .accordion__item__content--active {
10 | border-bottom: 1px solid #d4d8db;
11 | }
12 |
13 | &:last-child .accordion__item__content--active {
14 | border-top: 1px solid #d4d8db;
15 | }
16 |
17 | &__title {
18 | align-items: center;
19 | background-color: #f6fafc;
20 | display: flex;
21 | flex: 0 0 auto;
22 | font-size: 13px;
23 | height: 30px;
24 | justify-content: space-between;
25 | line-height: $lineHeight;
26 | padding: 0 10px;
27 | cursor: pointer;
28 | }
29 |
30 | &__content {
31 | background-color: #ffffff;
32 | display: none;
33 | flex: 1 1 auto;
34 | line-height: $lineHeight;
35 | padding: 5px 10px;
36 |
37 | &--active {
38 | animation: display-none-transition 1s both;
39 | display: flex;
40 | opacity: 1;
41 | }
42 |
43 | @keyframes display-none-transition {
44 | 0% {
45 | opacity: 0;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/framework/accordionItem/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'accordionItem';
3 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/AddOnControl.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as AddOnDropdown,
7 | } from './dropdown/AddOnDropdown.jsx';
8 |
9 | export {
10 | default as AddOnDropdownOption,
11 | } from './dropdown/AddOnDropdownOption.jsx';
12 |
13 | export {
14 | default as AddOnLabel,
15 | } from './label/AddOnLabel.jsx';
16 |
17 | const AddOnControl = props => (
18 |
19 | {props.children}
20 |
21 | );
22 |
23 | AddOnControl.propTypes = {
24 | children: PropTypes.any,
25 | };
26 |
27 | export default AddOnControl;
28 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/AddOnControl.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import AddOnControl from './AddOnControl.jsx';
5 |
6 | describe('AddOnControl', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered', () => {
10 | const props = {
11 | children: test
,
12 | };
13 | const testCase = TestCaseFactory.create(
14 | AddOnControl,
15 | props
16 | );
17 | expect(testCase.first('#test').textContent).toBe('test');
18 | });
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/_addOnControl.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin addOnLabelStyle {
3 | display: flex;
4 | align-items: center;
5 | font-size: 12px;
6 | font-weight: 400;
7 | color: #52676f;
8 | border: 1px solid #c8cfd5;
9 | background-color: #f2f5f7;
10 | }
11 |
12 | /**
13 | * 1. Fix a bug in IE that prevented the left and right element widths from
14 | * being respected by the input.
15 | */
16 | @mixin addOnLabelDimension {
17 | flex: 1 0 auto; /* 1 */
18 | }
19 |
20 | $addOnLabelPadding: 9px;
21 |
22 | /**
23 | * 1. Implicitly set these styles on inputs only, because they'll be overridden
24 | * for the left and right elements. These styles fix a bug in IE, so
25 | * that the input will contract to respect the left and right elements'
26 | * widths.
27 | */
28 | .addOnControl {
29 | display: flex;
30 | line-height: $lineHeight;
31 |
32 | > * {
33 | flex: 0 1 auto; /* 1 */
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'addOnControl';
3 | @import 'dropdown/addOnDropdown';
4 | @import 'dropdown/addOnDropdownOption';
5 | @import 'label/addOnLabel';
6 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/dropdown/AddOnDropdown.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import BaseDropdown from '../../base/dropdown/BaseDropdown.jsx';
7 | import AddOnDropdownOption from './AddOnDropdownOption.jsx';
8 |
9 | const AddOnDropdown = (props) => {
10 | const labelClasses = classNames(props.labelClasses, {
11 | 'addOnDropdownLabel--left': props.isLeftSide,
12 | 'addOnDropdownLabel--right': props.isRightSide,
13 | });
14 |
15 | return (
16 |
20 | );
21 | };
22 |
23 | AddOnDropdown.propTypes = Object.assign({}, BaseDropdown.propTypes, {
24 | isLeftSide: PropTypes.bool,
25 | isRightSide: PropTypes.bool,
26 | });
27 |
28 | AddOnDropdown.defaultProps = Object.assign({}, BaseDropdown.defaultProps, {
29 | classes: 'addOnDropdown',
30 | labelClasses: 'addOnDropdownLabel',
31 | inputClasses: 'addOnDropdown__input',
32 | labelFocusClasses: 'is-add-on-dropdown-label-focus',
33 | optionListClasses: 'addOnDropdownOptionList',
34 | optionType: AddOnDropdownOption,
35 | });
36 |
37 | export default AddOnDropdown;
38 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/dropdown/AddOnDropdownOption.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import BaseDropdownOption from '../../base/dropdown/BaseDropdownOption.jsx';
5 |
6 | const AddOnDropdownOption = props => (
7 |
10 | );
11 |
12 | AddOnDropdownOption.propTypes = BaseDropdownOption.propTypes;
13 |
14 | AddOnDropdownOption.defaultProps = {
15 | classes: 'addOnDropdownOption',
16 | focusClasses: 'is-add-on-dropdown-option-focus',
17 | };
18 |
19 | export default AddOnDropdownOption;
20 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/dropdown/AddOnDropdownOption.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import AddOnDropdownOption from './AddOnDropdownOption.jsx';
4 | import BaseDropdownOption from '../../base/dropdown/BaseDropdownOption.jsx';
5 |
6 | describe('AddOnDropdownOption', () => {
7 | describe('DOM structure', () => {
8 | it('is a BaseDropdownOption', () => {
9 | const props = {
10 | onClick: () => undefined,
11 | onMouseOver: () => undefined,
12 | };
13 | const testCase = TestCaseFactory.create(AddOnDropdownOption, props);
14 | expect(testCase.findComponents(BaseDropdownOption)).toBeDefined();
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/dropdown/_addOnDropdownOption.scss:
--------------------------------------------------------------------------------
1 |
2 | .addOnDropdownOption {
3 | cursor: pointer;
4 | font-size: 12px;
5 | padding: 4px 8px;
6 |
7 | &.is-add-on-dropdown-option-focus {
8 | background-color: rgba(black, 0.05);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/label/AddOnLabel.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const AddOnLabel = (props) => {
7 | const classes = classNames('addOnLabel', {
8 | 'addOnLabel--left': props.isLeftSide,
9 | 'addOnLabel--right': props.isRightSide,
10 | });
11 |
12 | return (
13 |
14 | {props.children}
15 |
16 | );
17 | };
18 |
19 | AddOnLabel.propTypes = {
20 | children: PropTypes.any,
21 | isLeftSide: PropTypes.bool,
22 | isRightSide: PropTypes.bool,
23 | };
24 |
25 | export default AddOnLabel;
26 |
--------------------------------------------------------------------------------
/src/framework/addOnControl/label/_addOnLabel.scss:
--------------------------------------------------------------------------------
1 |
2 | .addOnLabel {
3 | @include addOnLabelStyle;
4 | @include addOnLabelDimension;
5 | padding: $addOnLabelPadding;
6 | white-space: nowrap;
7 | }
8 |
9 | .addOnLabel--left {
10 | border-right: 0;
11 | }
12 |
13 | .addOnLabel--right {
14 | border-left: 0;
15 | }
16 |
--------------------------------------------------------------------------------
/src/framework/alert/Alert.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const Alert = (props) => {
8 | const typeToBackgroundColorClass = {
9 | [Alert.TYPE.ERROR]: 'alert--error',
10 | [Alert.TYPE.INFO]: 'alert--info',
11 | [Alert.TYPE.SUCCESS]: 'alert--success',
12 | [Alert.TYPE.WARNING]: 'alert--warning',
13 | };
14 |
15 | const classes = classNames(
16 | 'alert',
17 | props.classes,
18 | typeToBackgroundColorClass[props.type]
19 | );
20 |
21 | return (
22 |
23 | {props.children}
24 |
25 | );
26 | };
27 |
28 | Alert.TYPE = keyMirror({
29 | ERROR: null,
30 | INFO: null,
31 | SUCCESS: null,
32 | WARNING: null,
33 | });
34 |
35 | Alert.propTypes = {
36 | children: PropTypes.any,
37 | classes: PropTypes.string,
38 | type: PropTypes.string.isRequired,
39 | };
40 |
41 | export default Alert;
42 |
--------------------------------------------------------------------------------
/src/framework/alert/_alert.scss:
--------------------------------------------------------------------------------
1 |
2 | .alert {
3 | color: $white;
4 | padding: 12px;
5 | text-align: center;
6 | width: 100%;
7 |
8 | &.alert--error {
9 | background-color: $lightRed;
10 | }
11 |
12 | &.alert--info {
13 | background-color: $blue;
14 | }
15 |
16 | &.alert--success {
17 | background-color: $green;
18 | }
19 |
20 | &.alert--warning {
21 | background-color: $yellow;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/framework/alert/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'alert';
3 |
--------------------------------------------------------------------------------
/src/framework/appHeader/AppHeader.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as AccountNav,
7 | } from './account/AccountNav.jsx';
8 |
9 | export {
10 | default as AccountPicture,
11 | } from './account/AccountPicture.jsx';
12 |
13 | export {
14 | default as AppLogo,
15 | } from './logo/AppLogo.jsx';
16 |
17 | export {
18 | default as AppNav,
19 | } from './nav/AppNav.jsx';
20 |
21 | export {
22 | default as AppHeaderButton,
23 | } from './button/AppHeaderButton.jsx';
24 |
25 | export {
26 | default as AppHeaderDivider,
27 | } from './divider/AppHeaderDivider.jsx';
28 |
29 | export {
30 | default as AppTitle,
31 | } from './title/AppTitle.jsx';
32 |
33 | export {
34 | default as AppTitleContainer,
35 | } from './title/AppTitleContainer.jsx';
36 |
37 | const AppHeader = props => (
38 |
39 |
40 | {props.left}
41 | {props.center}
42 | {props.right}
43 |
44 |
45 | );
46 |
47 | AppHeader.propTypes = {
48 | left: PropTypes.element,
49 | center: PropTypes.element,
50 | right: PropTypes.element,
51 | };
52 |
53 | export default AppHeader;
54 |
--------------------------------------------------------------------------------
/src/framework/appHeader/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'appHeader';
3 | @import 'account/accountNav';
4 | @import 'account/accountPicture';
5 | @import 'logo/appLogo';
6 | @import 'nav/appNav';
7 | @import 'title/appTitle';
8 | @import 'title/appTitleContainer';
9 | @import 'button/appHeaderButton';
10 | @import 'divider/appHeaderDivider';
11 |
--------------------------------------------------------------------------------
/src/framework/appHeader/account/AccountPicture.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const AccountPicture = (props) => {
6 | const picture = props.url ?
7 | (
8 |
13 | ) : (
14 |
18 | );
19 |
20 | return (
21 |
22 | {picture}
23 |
24 | );
25 | };
26 |
27 | AccountPicture.propTypes = {
28 | url: PropTypes.string,
29 | title: PropTypes.string,
30 | };
31 |
32 | export default AccountPicture;
33 |
--------------------------------------------------------------------------------
/src/framework/appHeader/account/_accountPicture.scss:
--------------------------------------------------------------------------------
1 | .accountPicture {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | width: $accountPicture_side;
6 | height: $accountPicture_side;
7 | border-radius: $accountPicture_side / 2;
8 | // Affects vertical-alignment.
9 | // Icon is 1px off in .exampleContainer, when this is not set,
10 | // but always fine inside .appHeader
11 | line-height: $accountPicture_side;
12 | overflow: hidden;
13 | background: scale-lightness($appHeaderNav_backgroundColor, 10%);
14 | }
15 |
16 | .accountPicture__icon {
17 | width: 10px;
18 | height: 10px;
19 | opacity: 0.5;
20 | }
21 |
--------------------------------------------------------------------------------
/src/framework/appHeader/button/AppHeaderButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import Button from '../../button/Button.jsx';
7 |
8 | const AppHeaderButton = (props) => {
9 | const classes = classNames('appHeaderButton', props.classes);
10 |
11 | const extendedProps = Object.assign({}, props, {
12 | classes,
13 | });
14 |
15 | return (
16 |
17 | );
18 | };
19 |
20 | AppHeaderButton.propTypes = Object.assign({}, Button.propTypes, {
21 | classes: PropTypes.string,
22 | });
23 |
24 | export default AppHeaderButton;
25 |
--------------------------------------------------------------------------------
/src/framework/appHeader/button/AppHeaderButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import AppHeaderButton from './AppHeaderButton.jsx';
4 |
5 | describe('AppHeaderButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(AppHeaderButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('appHeaderButton');
10 | });
11 |
12 | it('is a Button', () => {
13 | const testCase =
14 | TestCaseFactory.create(AppHeaderButton);
15 | expect(testCase.dom.className).toContain('button');
16 | });
17 | });
18 |
19 | describe('Props', () => {
20 | describe('classes', () => {
21 | it('are added to the element', () => {
22 | const props = {
23 | classes: 'test-class',
24 | };
25 | const testCase = TestCaseFactory.create(
26 | AppHeaderButton,
27 | props
28 | );
29 | expect(testCase.dom.getAttribute('class')).toContain(props.classes);
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/src/framework/appHeader/button/_appHeaderButton.scss:
--------------------------------------------------------------------------------
1 | $appHeaderButtonColor: #445e6a;
2 |
3 | .appHeaderButton {
4 | @include button;
5 | margin-left: 5px;
6 | background: $appHeaderButtonColor;
7 | color: #ffffff;
8 | border-color: $appHeaderButtonColor;
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/appHeader/divider/AppHeaderDivider.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const AppHeaderDivider = props => (
6 |
7 | {props.left}
8 |
9 | {props.right}
10 |
11 | );
12 |
13 | AppHeaderDivider.propTypes = {
14 | left: PropTypes.element.isRequired,
15 | right: PropTypes.element.isRequired,
16 | };
17 |
18 | export default AppHeaderDivider;
19 |
--------------------------------------------------------------------------------
/src/framework/appHeader/divider/_appHeaderDivider.scss:
--------------------------------------------------------------------------------
1 |
2 | $appHeaderDivider_color: #26373e;
3 |
4 | .appHeaderDivider {
5 | align-items: center;
6 | display: flex;
7 | }
8 |
9 | .appHeaderDivider__divider {
10 | background-color: $appHeaderDivider_color;
11 | height: 15px;
12 | width: 1px;
13 | margin: 0 10px;
14 | }
15 |
--------------------------------------------------------------------------------
/src/framework/appHeader/logo/AppLogo.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const AppLogo = (props) => {
6 | const attributes = {
7 | className: 'appLogo',
8 | title: props.text,
9 | };
10 |
11 | if (props.href) {
12 | return (
13 |
14 | {props.text}
15 |
16 | );
17 | }
18 |
19 | return (
20 |
21 | {props.text}
22 |
23 | );
24 | };
25 |
26 | AppLogo.propTypes = {
27 | href: PropTypes.string,
28 | text: PropTypes.string.isRequired,
29 | };
30 |
31 | export default AppLogo;
32 |
--------------------------------------------------------------------------------
/src/framework/appHeader/logo/_appLogo.scss:
--------------------------------------------------------------------------------
1 | .appLogo {
2 | display: block;
3 | text-indent: -999em;
4 | // This is to cut logo image on a smaller screen properly
5 | width: 76px;
6 | height: 17px;
7 | background-image: url($logo_76px);
8 | background-size: 76px 17px;
9 | background-position: right top;
10 | background-repeat: no-repeat;
11 |
12 | @media #{$viewport_normalScreen} {
13 | width: 17px;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/framework/appHeader/nav/AppNav.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import classNames from 'classnames';
6 |
7 | const AppNav = (props) => {
8 | const links = props.links.map((link, index) => (
9 | React.createElement(props.linkType, Object.assign({}, link, {
10 | className: classNames('appNav__link', link.className),
11 | key: index,
12 | }))
13 | ));
14 |
15 | return (
16 |
19 | );
20 | };
21 |
22 | AppNav.propTypes = {
23 | linkType: PropTypes.oneOfType([
24 | PropTypes.string,
25 | PropTypes.func,
26 | ]).isRequired,
27 | links: PropTypes.arrayOf(PropTypes.object).isRequired,
28 | };
29 |
30 | AppNav.ACTIVE_LINK_CLASS_NAME = 'is-app-nav-link-selected';
31 |
32 | export default AppNav;
33 |
--------------------------------------------------------------------------------
/src/framework/appHeader/nav/_appNav.scss:
--------------------------------------------------------------------------------
1 | .appNav {
2 | display: flex;
3 | height: inherit;
4 | }
5 |
6 | .appNav__link {
7 | // This helps to inherit height from parent
8 | display: flex;
9 | color: $appHeaderNav_textColor;
10 | align-items: center;
11 | text-transform: uppercase;
12 | font-size: 13px;
13 | padding: 0 24px;
14 | font-weight: 400;
15 | transition: 0.2s padding ease-in-out, 0.06s background ease-in-out;
16 |
17 | @media #{$viewport_normalScreen} {
18 | padding: 0 14px;
19 | font-size: 12px;
20 | }
21 |
22 | &:hover {
23 | background: scale-lightness($appHeaderNav_backgroundColor, 5%);
24 | }
25 |
26 | &.is-app-nav-link-selected {
27 | cursor: default;
28 | background: $appHeaderNav_selectedColor;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/AppTitle.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const AppTitle = props => (
6 |
9 | {props.text}
10 |
11 | );
12 |
13 | AppTitle.propTypes = {
14 | text: PropTypes.string.isRequired,
15 | };
16 |
17 | export default AppTitle;
18 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/AppTitle.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import AppTitle from './AppTitle.jsx';
4 |
5 | describe('AppTitle', () => {
6 | describe('Props', () => {
7 | describe('text', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | text: 'Test',
11 | };
12 |
13 | const testCase = TestCaseFactory.create(AppTitle, props);
14 |
15 | expect(testCase.dom.textContent).toBe(props.text);
16 | });
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/AppTitleContainer.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import AppTitle from './AppTitle.jsx';
6 |
7 | const AppTitleContainer = props => (
8 |
9 |
16 |
17 | {props.children}
18 |
19 |
20 | );
21 |
22 | AppTitleContainer.propTypes = {
23 | children: PropTypes.any,
24 | onClick: PropTypes.func,
25 | title: AppTitle.propTypes.text,
26 | };
27 |
28 | export default AppTitleContainer;
29 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/AppTitleContainer.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import AppTitleContainer from './AppTitleContainer.jsx';
4 |
5 | describe('AppTitleContainer', () => {
6 | describe('Props', () => {
7 | describe('onClick', () => {
8 | it('is called when the title element is clicked', () => {
9 | const props = {
10 | onClick: jasmine.createSpy('onClick'),
11 | title: 'Title text',
12 | };
13 |
14 | const testCase = TestCaseFactory.create(
15 | AppTitleContainer,
16 | props
17 | );
18 | expect(props.onClick).not.toHaveBeenCalled();
19 | const titleEl = testCase.first('.appTitleContainer__title');
20 | testCase.trigger('click', titleEl);
21 | expect(props.onClick).toHaveBeenCalled();
22 | });
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/_appTitle.scss:
--------------------------------------------------------------------------------
1 |
2 | .appTitle {
3 | display: inline-block;
4 | color: $appHeaderNav_textColor;
5 | }
6 |
--------------------------------------------------------------------------------
/src/framework/appHeader/title/_appTitleContainer.scss:
--------------------------------------------------------------------------------
1 | .appTitleContainer {
2 | position: relative;
3 | line-height: $lineHeight;
4 | }
5 |
6 | .appTitleContainer__title {
7 | cursor: pointer;
8 | display: flex;
9 | align-items: center;
10 | }
11 |
12 | .appTitleContainer__target {
13 | position: absolute;
14 | top: -6px;
15 | left: 0;
16 | z-index: 10;
17 | }
18 |
--------------------------------------------------------------------------------
/src/framework/base/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'dropdown/index';
3 |
--------------------------------------------------------------------------------
/src/framework/base/dropdown/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Position some children absolutely.
4 | */
5 | @mixin baseDropdown {
6 | position: relative; /* 1 */
7 | }
8 |
9 | /**
10 | * 1. Position beneath other elements because we want this input visually hidden
11 | * to the user but still accessible via tab.
12 | * 2. Move it down and to the right a bit to make sure it's completely covered.
13 | * 3. Also make it invisible just for the heck of it.
14 | */
15 | @mixin baseDropdownInput {
16 | position: absolute; /* 1 */
17 | z-index: 0; /* 1 */
18 | width: 0; /* 1 */
19 | height: 0; /* 1 */
20 | top: 6px; /* 2 */
21 | left: 6px; /* 2 */
22 | opacity: 0; /* 3 */
23 | }
24 |
25 | /**
26 | * 1. Put the label on top of the input to hide it.
27 | */
28 | @mixin baseDropdownLabel {
29 | position: relative; /* 1 */
30 | z-index: 1; /* 1 */
31 | }
32 |
33 | /**
34 | * 1. Put the option list on top of the label, so the label doesn't hide it.
35 | */
36 | @mixin baseDropdownOptionList {
37 | position: absolute; /* 1 */
38 | z-index: 2; /* 1 */
39 | max-height: 300px;
40 | overflow-y: auto;
41 | }
42 |
--------------------------------------------------------------------------------
/src/framework/body/Body.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | export {
7 | default as BodyMaxWidthLayout,
8 | } from './BodyMaxWidthLayout.jsx';
9 |
10 | export {
11 | default as BodyPanel,
12 | } from './BodyPanel.jsx';
13 |
14 | export {
15 | default as BodyPanelItem,
16 | } from './BodyPanelItem.jsx';
17 |
18 | const Body = (props) => {
19 | const classes = classNames('body', {
20 | 'body--dark': props.isDark,
21 | });
22 |
23 | return (
24 |
25 | {props.children}
26 |
27 | );
28 | };
29 |
30 | Body.propTypes = {
31 | children: PropTypes.any,
32 | isDark: PropTypes.bool,
33 | };
34 |
35 | export default Body;
36 |
--------------------------------------------------------------------------------
/src/framework/body/Body.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import Body from './Body.jsx';
5 |
6 | describe('Body', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered', () => {
10 | const props = {
11 | children: test
,
12 | };
13 | const testCase = TestCaseFactory.create(Body, props);
14 | expect(testCase.first('#test').textContent).toBe('test');
15 | });
16 | });
17 |
18 | describe('isDark', () => {
19 | it('applies the appropriate class', () => {
20 | const props = {
21 | children: '',
22 | isDark: true,
23 | };
24 | const testCase = TestCaseFactory.create(Body, props);
25 | expect(testCase.dom.className).toContain('body--dark');
26 | });
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/framework/body/BodyMaxWidthLayout.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const BodyMaxWidthLayout = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | BodyMaxWidthLayout.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default BodyMaxWidthLayout;
16 |
--------------------------------------------------------------------------------
/src/framework/body/BodyMaxWidthLayout.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import BodyMaxWidthLayout from './BodyMaxWidthLayout.jsx';
4 |
5 | describe('BodyMaxWidthLayout', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'test',
11 | };
12 | const testCase = TestCaseFactory.create(BodyMaxWidthLayout, props);
13 | expect(testCase.dom.textContent).toBe(props.children);
14 | });
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/body/BodyPanel.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import Box from '../box/Box.jsx';
7 |
8 | const BodyPanel = (props) => {
9 | const classes = classNames('bodyPanel', props.panelClasses, {
10 | 'bodyPanel--bottomFlush': props.isBottomFlush,
11 | 'bodyPanel--topFlush': props.isTopFlush,
12 | });
13 |
14 | return (
15 |
19 | {props.children}
20 |
21 | );
22 | };
23 |
24 | BodyPanel.propTypes = {
25 | children: Box.propTypes.children,
26 | isBottomFlush: PropTypes.bool,
27 | isTopFlush: PropTypes.bool,
28 | panelClasses: PropTypes.string,
29 | roundedCorners: Box.propTypes.roundedCorners,
30 | };
31 |
32 | export default BodyPanel;
33 |
--------------------------------------------------------------------------------
/src/framework/body/BodyPanel.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import BodyPanel from './BodyPanel.jsx';
5 |
6 | describe('BodyPanel', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered', () => {
10 | const props = {
11 | children: test
,
12 | };
13 | const testCase = TestCaseFactory.create(BodyPanel, props);
14 | expect(testCase.first('#test').textContent).toBe('test');
15 | });
16 | });
17 |
18 | describe('isTopFlush', () => {
19 | it('applies the appropriate class', () => {
20 | const props = {
21 | isTopFlush: true,
22 | };
23 | const testCase = TestCaseFactory.create(BodyPanel, props);
24 | expect(testCase.dom.className).toContain('bodyPanel--topFlush');
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/body/BodyPanelItem.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const BodyPanelItem = (props) => {
7 | const rhythmClassMap = {
8 | [BodyPanelItem.RHYTHM.LARGE]: 'bodyPanelItem--largeRhythm',
9 | };
10 |
11 | const classes = classNames('bodyPanelItem', rhythmClassMap[props.rhythm]);
12 |
13 | return (
14 |
15 | {props.children}
16 |
17 | );
18 | };
19 |
20 | BodyPanelItem.RHYTHM = {
21 | LARGE: 'LARGE',
22 | };
23 |
24 | BodyPanelItem.propTypes = {
25 | children: PropTypes.any,
26 | rhythm: PropTypes.string,
27 | };
28 |
29 | export default BodyPanelItem;
30 |
--------------------------------------------------------------------------------
/src/framework/body/BodyPanelItem.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import BodyPanelItem from './BodyPanelItem.jsx';
5 |
6 | describe('BodyPanelItem', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered', () => {
10 | const props = {
11 | children: test
,
12 | };
13 | const testCase = TestCaseFactory.create(BodyPanelItem, props);
14 | expect(testCase.first('#test').textContent).toBe('test');
15 | });
16 | });
17 |
18 | describe('rhythm', () => {
19 | describe('LARGE', () => {
20 | it('adds the appropriate class', () => {
21 | const props = {
22 | rhythm: BodyPanelItem.RHYTHM.LARGE,
23 | };
24 |
25 | const testCase = TestCaseFactory.create(BodyPanelItem, props);
26 | expect(testCase.dom.className)
27 | .toContain('bodyPanelItem--largeRhythm');
28 | });
29 | });
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/framework/body/_body.scss:
--------------------------------------------------------------------------------
1 |
2 | .body--noscroll {
3 | overflow: hidden;
4 | }
5 |
6 | /**
7 | * 1. Ensure body element is at least as tall as the window.
8 | */
9 | .body {
10 | display: flex;
11 | flex-direction: column;
12 | justify-content: flex-start;
13 | align-items: center;
14 | min-height: 100vh; /* 1 */
15 | background-color: #ebf3f5;
16 | }
17 |
18 | .body--dark {
19 | background-color: $darkGrey;
20 | }
21 |
--------------------------------------------------------------------------------
/src/framework/body/_bodyMaxWidthLayout.scss:
--------------------------------------------------------------------------------
1 |
2 | .bodyMaxWidthLayout {
3 | max-width: $maxWidth;
4 | width: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/src/framework/body/_bodyPanel.scss:
--------------------------------------------------------------------------------
1 |
2 | .bodyPanel {
3 | margin-top: 15px;
4 | max-width: $maxWidth;
5 | padding-bottom: 15px;
6 | padding-top: 15px;
7 | width: 100%;
8 |
9 | &.bodyPanel--bottomFlush {
10 | border-bottom: 0;
11 | margin-bottom: 0;
12 | }
13 |
14 | &.bodyPanel--topFlush {
15 | border-top: 0;
16 | margin-top: 0;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/framework/body/_bodyPanelItem.scss:
--------------------------------------------------------------------------------
1 | .bodyPanelItem {
2 | padding-left: 30px;
3 | padding-right: 30px;
4 | }
5 |
6 | .bodyPanelItem--largeRhythm {
7 | margin-bottom: 30px;
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/body/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'body';
2 | @import 'bodyMaxWidthLayout';
3 | @import 'bodyPanel';
4 | @import 'bodyPanelItem';
5 |
--------------------------------------------------------------------------------
/src/framework/box/Box.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const Box = (props) => {
7 | const classes = classNames('box', props.classes, {
8 | 'box--roundedCorners': props.roundedCorners,
9 | });
10 |
11 | return (
12 |
16 | {props.children}
17 |
18 | );
19 | };
20 |
21 | Box.propTypes = {
22 | children: PropTypes.any,
23 | classes: PropTypes.string,
24 | dataId: PropTypes.string,
25 | roundedCorners: PropTypes.bool,
26 | };
27 |
28 | export default Box;
29 |
--------------------------------------------------------------------------------
/src/framework/box/_box.scss:
--------------------------------------------------------------------------------
1 |
2 | .box {
3 | background-color: #ffffff;
4 | border: 1px solid $borderGrey;
5 | }
6 |
7 | .box--roundedCorners {
8 | border-radius: 5px;
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/box/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'box';
3 |
--------------------------------------------------------------------------------
/src/framework/button/AlertButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import React from 'react';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const AlertButton = (props) => {
8 | const classes = classNames('button--alert', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | AlertButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | AlertButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default AlertButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/AlertButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import AlertButton from './AlertButton.jsx';
4 |
5 | describe('AlertButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(AlertButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--alert');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const classes = 'test-class';
17 | const props = {
18 | classes,
19 | };
20 | const testCase = TestCaseFactory.create(
21 | AlertButton,
22 | props
23 | );
24 | expect(testCase.dom.getAttribute('class')).toContain(classes);
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/button/BasicButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const BasicButton = (props) => {
8 | const classes = classNames('button--basic', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | BasicButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | BasicButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default BasicButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/BasicButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import BasicButton from './BasicButton.jsx';
4 |
5 | describe('BasicButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(BasicButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--basic');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const props = {
17 | classes: 'testClass',
18 | };
19 | const testCase = TestCaseFactory.create(BasicButton, props);
20 | expect(testCase.dom.className).toContain(props.classes);
21 | });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/framework/button/CallOutButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const CallOutButton = (props) => {
8 | const classes = classNames('button--callOut', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | CallOutButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | CallOutButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default CallOutButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/CallOutButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import CallOutButton from './CallOutButton.jsx';
4 |
5 | describe('CallOutButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(CallOutButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--callOut');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const classes = 'test-class';
17 | const props = {
18 | classes,
19 | };
20 | const testCase = TestCaseFactory.create(
21 | CallOutButton,
22 | props
23 | );
24 | expect(testCase.dom.getAttribute('class')).toContain(classes);
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/button/GroupedButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const GroupedButton = (props) => {
8 | const classes = classNames('button--grouped', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | GroupedButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | GroupedButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default GroupedButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/GroupedButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import GroupedButton from './GroupedButton.jsx';
4 |
5 | describe('GroupedButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(GroupedButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--grouped');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const props = {
17 | classes: 'testClass',
18 | };
19 | const testCase =
20 | TestCaseFactory.create(GroupedButton, props);
21 | expect(testCase.dom.className).toContain(props.classes);
22 | });
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/framework/button/HollowButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const HollowButton = (props) => {
8 | const classes = classNames('button--hollow', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | HollowButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | HollowButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default HollowButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/HollowButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import HollowButton from './HollowButton.jsx';
4 |
5 | describe('HollowButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(HollowButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--hollow');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const classes = 'test-class';
17 | const props = {
18 | classes,
19 | };
20 | const testCase = TestCaseFactory.create(
21 | HollowButton,
22 | props
23 | );
24 | expect(testCase.dom.getAttribute('class')).toContain(classes);
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/button/PrimaryButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | import Button from './Button.jsx';
6 |
7 | const PrimaryButton = (props) => {
8 | const classes = classNames('button--primary', props.classes);
9 |
10 | const extendedProps = Object.assign({}, props, {
11 | classes,
12 | });
13 |
14 | return (
15 |
16 | );
17 | };
18 |
19 | PrimaryButton.TYPE = Object.assign({}, Button.TYPE);
20 |
21 | PrimaryButton.propTypes = Object.assign({}, Button.propTypes);
22 |
23 | export default PrimaryButton;
24 |
--------------------------------------------------------------------------------
/src/framework/button/PrimaryButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import PrimaryButton from './PrimaryButton.jsx';
4 |
5 | describe('PrimaryButton', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(PrimaryButton);
9 | expect(testCase.dom.getAttribute('class')).toContain('button--primary');
10 | });
11 | });
12 |
13 | describe('Props', () => {
14 | describe('classes', () => {
15 | it('are added to the element', () => {
16 | const classes = 'test-class';
17 | const props = {
18 | classes,
19 | };
20 | const testCase = TestCaseFactory.create(
21 | PrimaryButton,
22 | props
23 | );
24 | expect(testCase.dom.getAttribute('class')).toContain(classes);
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/button/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'button';
2 |
--------------------------------------------------------------------------------
/src/framework/button/index.jsx:
--------------------------------------------------------------------------------
1 | export {
2 | default as Button,
3 | } from './Button.jsx';
4 |
5 | export {
6 | default as AlertButton,
7 | } from './AlertButton.jsx';
8 |
9 | export {
10 | default as BasicButton,
11 | } from './BasicButton.jsx';
12 |
13 | export {
14 | default as CallOutButton,
15 | } from './CallOutButton.jsx';
16 |
17 | export {
18 | default as GroupedButton,
19 | } from './GroupedButton.jsx';
20 |
21 | export {
22 | default as HollowButton,
23 | } from './HollowButton.jsx';
24 |
25 | export {
26 | default as PrimaryButton,
27 | } from './PrimaryButton.jsx';
28 |
--------------------------------------------------------------------------------
/src/framework/buttonGroup/ButtonGroup.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ButtonGroup = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | ButtonGroup.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default ButtonGroup;
16 |
--------------------------------------------------------------------------------
/src/framework/buttonGroup/ButtonGroup.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ButtonGroup from './ButtonGroup.jsx';
4 |
5 | describe('ButtonGroup', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'test',
11 | };
12 | const testCase = TestCaseFactory.create(
13 | ButtonGroup,
14 | props
15 | );
16 | expect(testCase.dom.textContent).toBe(props.children);
17 | });
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/framework/buttonGroup/_buttonGroup.scss:
--------------------------------------------------------------------------------
1 |
2 | .buttonGroup {
3 | .button {
4 | &:not(:last-child) {
5 | border-right-width: 0;
6 | }
7 |
8 | &:not(:first-child):not(:last-child) {
9 | border-radius: 0;
10 | }
11 |
12 | &:first-child {
13 | border-top-right-radius: 0;
14 | border-bottom-right-radius: 0;
15 | }
16 |
17 | &:last-child {
18 | border-top-left-radius: 0;
19 | border-bottom-left-radius: 0;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/framework/buttonGroup/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'buttonGroup';
2 |
--------------------------------------------------------------------------------
/src/framework/card/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'card';
3 |
--------------------------------------------------------------------------------
/src/framework/cardHolder/CardHolder.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const CardHolder = (props) => {
6 | const holderStyle = {
7 | gridTemplateColumns:
8 | `repeat(auto-fit, minmax(${props.childrenMinWidth}, 1fr))`,
9 | };
10 | const content = props.children.map((child, index) => (
11 |
12 | {child}
13 |
14 | ));
15 | if (props.amountPerRow) {
16 | while (content.length < props.amountPerRow) {
17 | content.push(
18 |
19 | );
20 | }
21 | }
22 |
23 | return (
24 |
25 | {content}
26 |
27 | );
28 | };
29 |
30 | CardHolder.propTypes = {
31 | amountPerRow: PropTypes.number,
32 | children: PropTypes.arrayOf(PropTypes.element),
33 | childrenMinWidth: PropTypes.string,
34 | };
35 |
36 | CardHolder.defaultProps = {
37 | childrenMinWidth: '220px',
38 | };
39 |
40 | export default CardHolder;
41 |
--------------------------------------------------------------------------------
/src/framework/cardHolder/_cardHolder.scss:
--------------------------------------------------------------------------------
1 |
2 | .cardHolder {
3 | display: grid;
4 |
5 | &__wrapper {
6 | justify-self: center;
7 | margin-bottom: 10px;
8 | margin-top: 10px;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/framework/cardHolder/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'cardHolder';
3 |
--------------------------------------------------------------------------------
/src/framework/changeLog/ChangeLogLink.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import HollowButton from '../button/HollowButton.jsx';
4 |
5 | const ChangeLogLink = props => (
6 |
7 |
12 | Change Log
13 |
14 |
15 | );
16 |
17 | ChangeLogLink.propTypes = {
18 | onClick: PropTypes.func,
19 | };
20 |
21 | export default ChangeLogLink;
22 |
--------------------------------------------------------------------------------
/src/framework/changeLog/ChangeLogLink.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import { TestCaseFactory } from 'react-test-kit';
5 |
6 | import ChangeLogLink from './ChangeLogLink.jsx';
7 |
8 | describe('ChangeLogLink', () => {
9 | describe('Props', () => {
10 | describe('onClick', () => {
11 | it('is called when the button is clicked', () => {
12 | const props = {
13 | changes: [],
14 | onClick: jasmine.createSpy(),
15 | };
16 |
17 | const testCase = TestCaseFactory.createFromElementWithWrapper(
18 |
19 | );
20 |
21 | testCase.trigger('click', testCase.first('.button--hollow'));
22 |
23 | expect(props.onClick).toHaveBeenCalled();
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/framework/changeLog/_changeLogLink.scss:
--------------------------------------------------------------------------------
1 |
2 | .changeLogLink {
3 | flex-grow: 1;
4 | text-align: right;
5 | }
6 |
--------------------------------------------------------------------------------
/src/framework/changeLog/_changeLogModal.scss:
--------------------------------------------------------------------------------
1 |
2 | .changeLogModalChangeIcon {
3 | display: inline-block;
4 | height: 17px;
5 | margin-right: 5px;
6 | width: 17px;
7 | vertical-align: middle;
8 | }
9 |
10 | .changeLogModalChangeTime {
11 | font-weight: 700;
12 | vertical-align: middle;
13 | }
14 |
15 | .changeLogModalChangeText {
16 | line-height: 1.3;
17 | }
18 |
--------------------------------------------------------------------------------
/src/framework/changeLog/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'changeLogLink';
3 | @import 'changeLogModal';
4 |
--------------------------------------------------------------------------------
/src/framework/changeLog/helpers.js:
--------------------------------------------------------------------------------
1 | export function isValidValue(value) {
2 | if (value === undefined || value === null || value === '') {
3 | return false;
4 | }
5 | if (Array.isArray(value)) {
6 | return value.length;
7 | }
8 | return true;
9 | }
10 |
11 | export function getType(oldValue, newValue) {
12 | if (isValidValue(oldValue) && !isValidValue(newValue)) {
13 | return 'D';
14 | }
15 | if (!isValidValue(oldValue) && isValidValue(newValue)) {
16 | return 'I';
17 | }
18 | return 'U';
19 | }
20 |
--------------------------------------------------------------------------------
/src/framework/chart/_chart.scss:
--------------------------------------------------------------------------------
1 |
2 | .chart {
3 | padding: 20px;
4 | }
5 |
6 | .chart__legend {
7 | float: right;
8 | font-size: 12px;
9 | font-weight: 400;
10 |
11 | > span {
12 | margin-left: 20px;
13 |
14 | &:first-child {
15 | margin-left: auto;
16 | }
17 | }
18 | }
19 |
20 | .descriptionText {
21 | margin-top: 10px;
22 | }
23 |
24 | .chart__lineChart {
25 | margin-top: 20px;
26 | position: relative;
27 |
28 | .descriptionText + & {
29 | margin-top: 10px;
30 | }
31 |
32 | &.chart__lineChart--blurred .lineChart {
33 | @include backgroundBlur;
34 | }
35 |
36 | .progress {
37 | left: 50%;
38 | margin-right: -50%;
39 | position: absolute;
40 | top: 50%;
41 | transform: translate(-50%, -50%);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/framework/chart/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'chart';
3 | @import 'lineChart';
4 | @import 'chartDot/chartDot';
5 |
--------------------------------------------------------------------------------
/src/framework/chart/_lineChart.scss:
--------------------------------------------------------------------------------
1 |
2 | .lineChart {
3 | position: relative;
4 | }
5 |
6 | .lineChartAxisTick__mark {
7 | shape-rendering: crispEdges;
8 | stroke: #e5e7e8;
9 | }
10 |
11 | .lineChartAxisTick__text {
12 | fill: $textColor;
13 | font-size: 11px;
14 | }
15 |
16 | .lineChartLine {
17 | fill: none;
18 | stroke-width: 2px;
19 | }
20 |
21 | .lineChart__tooltip {
22 | display: none;
23 | position: absolute;
24 | opacity: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/src/framework/chart/chartDot/ChartDot.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ChartDot = props => (
6 |
13 | );
14 |
15 | ChartDot.propTypes = {
16 | color: PropTypes.string,
17 | };
18 |
19 | export default ChartDot;
20 |
--------------------------------------------------------------------------------
/src/framework/chart/chartDot/ChartDot.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ChartDot from './ChartDot.jsx';
4 |
5 | describe('ChartDot', () => {
6 | describe('Props', () => {
7 | describe('color', () => {
8 | it('is applied to the dot', () => {
9 | const props = {
10 | color: 'white',
11 | };
12 | const testCase = TestCaseFactory.create(ChartDot, props);
13 | const dotStyle = testCase.dom.getAttribute('style').toLowerCase();
14 |
15 | expect(dotStyle).toContain(`background-color: ${props.color}`);
16 | expect(dotStyle).toContain(`border: 1px solid ${props.color}`);
17 | });
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/framework/chart/chartDot/_chartDot.scss:
--------------------------------------------------------------------------------
1 |
2 | $chartDotSize: 10px;
3 |
4 | .chartDot {
5 | border-radius: $chartDotSize / 2;
6 | display: inline-block;
7 | height: $chartDotSize;
8 | width: $chartDotSize;
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/checkBox/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'checkBox';
3 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/Column.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const Column = (props) => {
7 | const classes = classNames('column', `column--${props.width}`);
8 |
9 | return (
10 |
13 | {props.children}
14 |
15 | );
16 | };
17 |
18 | Column.propTypes = {
19 | width: PropTypes.number.isRequired,
20 | children: PropTypes.any,
21 | };
22 |
23 | export default Column;
24 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/Column.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import Column from './Column.jsx';
5 |
6 | describe('Column', () => {
7 | describe('Props', () => {
8 | describe('width', () => {
9 | it('applies the appropriate class', () => {
10 | const props = {
11 | width: 1,
12 | children: ,
13 | };
14 |
15 | const testCase = TestCaseFactory.create(Column, props);
16 | expect(testCase.dom.className).toContain('column--1');
17 | });
18 | });
19 |
20 | describe('children', () => {
21 | it('are rendered', () => {
22 | const props = {
23 | width: 1,
24 | children: ,
25 | };
26 |
27 | const testCase = TestCaseFactory.create(Column, props);
28 | expect(testCase.first('#child')).toBeDefined();
29 | });
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/ColumnLayout.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as Column,
7 | } from './Column.jsx';
8 |
9 | const ColumnLayout = props => (
10 |
11 | {props.children}
12 |
13 | );
14 |
15 | ColumnLayout.propTypes = {
16 | children: PropTypes.any,
17 | };
18 |
19 | export default ColumnLayout;
20 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/ColumnLayout.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ColumnLayout from './ColumnLayout.jsx';
4 |
5 | describe('ColumnLayout', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'test',
11 | };
12 |
13 | const testCase = TestCaseFactory.create(ColumnLayout, props);
14 | expect(testCase.dom.textContent).toBe(props.children);
15 | });
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/_columnLayout.scss:
--------------------------------------------------------------------------------
1 |
2 | .column + .column {
3 | padding-left: 10px;
4 | }
5 |
6 | /**
7 | * 1. Use inline-block instead of flexbox so that content doesn't overflow.
8 | * 2. Content can be aligned by offsetting from the top.
9 | */
10 | $numColumns: 12;
11 | @for $i from 1 through $numColumns {
12 | .column--#{$i} {
13 | display: inline-block; /* 1 */
14 | vertical-align: top; /* 2 */
15 | width: $i / $numColumns * 100%;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/framework/columnLayout/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'columnLayout';
2 |
--------------------------------------------------------------------------------
/src/framework/dropdown/DropdownGroup.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 |
4 | import Dropdown from './Dropdown.jsx';
5 | import DropdownGroup from './DropdownGroup.jsx';
6 |
7 | describe('DropdownGroup', () => {
8 | const requiredProps = {
9 | labelProvider: () => undefined,
10 | options: [['blue', 'pink'], ['black', 'white']],
11 | optionGroupClasses: ['', 'dropdownClass'],
12 | };
13 |
14 | describe('DOM structure', () => {
15 | it('is a Dropdown', () => {
16 | const props = Object.assign({}, requiredProps);
17 | const testCase = TestCaseFactory.create(DropdownGroup, props);
18 | expect(testCase.firstComponent(Dropdown)).toBeDefined();
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/framework/dropdown/DropdownOption.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import React from 'react';
4 |
5 | import BaseDropdownOption from '../base/dropdown/BaseDropdownOption.jsx';
6 |
7 | const DropdownOption = (props) => {
8 | const classes = classNames('dropdownOption', props.classes);
9 | const extendedProps = Object.assign({}, props, {
10 | classes,
11 | });
12 |
13 | return (
14 |
17 | );
18 | };
19 |
20 | DropdownOption.propTypes = BaseDropdownOption.propTypes;
21 |
22 | DropdownOption.defaultProps = {
23 | focusClasses: 'is-dropdown-option-focus',
24 | };
25 |
26 | export default DropdownOption;
27 |
--------------------------------------------------------------------------------
/src/framework/dropdown/DropdownOption.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import DropdownOption from './DropdownOption.jsx';
4 | import BaseDropdownOption from '../base/dropdown/BaseDropdownOption.jsx';
5 |
6 | describe('DropdownOption', () => {
7 | describe('DOM structure', () => {
8 | it('is a BaseDropdownOption', () => {
9 | const props = {
10 | onClick: () => undefined,
11 | onMouseOver: () => undefined,
12 | };
13 | const testCase = TestCaseFactory.create(DropdownOption, props);
14 | expect(testCase.findComponents(BaseDropdownOption)).toBeDefined();
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/dropdown/_dropdownOption.scss:
--------------------------------------------------------------------------------
1 |
2 | .dropdownOption {
3 | cursor: pointer;
4 | font-size: 12px;
5 | height: 26px;
6 | line-height: 18px;
7 | padding: 4px 8px;
8 |
9 | &.is-dropdown-option-focus {
10 | background-color: #f3f9fd;
11 | }
12 |
13 | &.dropdownGroup--first {
14 | border-top: 1px dashed rgba(#7a8183, 0.4);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/framework/dropdown/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'dropdown';
3 | @import 'dropdownOption';
4 | @import 'dropdownDot/index';
5 |
--------------------------------------------------------------------------------
/src/framework/dropdown/dropdownDot/DropdownDot.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const DropdownDot = (props) => {
8 | const classMap = {
9 | [DropdownDot.COLOR.BLUE]: 'dropdownDot--blue',
10 | [DropdownDot.COLOR.GREEN]: 'dropdownDot--green',
11 | [DropdownDot.COLOR.GREY]: 'dropdownDot--grey',
12 | [DropdownDot.COLOR.RED]: 'dropdownDot--red',
13 | };
14 |
15 | const labelClasses = classNames('dropdownDot', classMap[props.color]);
16 |
17 | return (
18 |
19 | );
20 | };
21 |
22 | DropdownDot.COLOR = keyMirror({
23 | BLUE: null,
24 | GREEN: null,
25 | GREY: null,
26 | RED: null,
27 | });
28 |
29 | DropdownDot.propTypes = {
30 | color: PropTypes.string,
31 | };
32 |
33 | export default DropdownDot;
34 |
--------------------------------------------------------------------------------
/src/framework/dropdown/dropdownDot/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | .dropdownDot {
3 | $dropdownDotSize: 10px;
4 | background-color: $dropdownDotGreyFill;
5 | border-radius: 100%;
6 | border: 1px solid $dropdownDotGreyLine;
7 | display: inline-block;
8 | height: $dropdownDotSize;
9 | width: $dropdownDotSize;
10 | }
11 |
12 | .dropdownDot--blue {
13 | background-color: $dropdownDotBlueFill;
14 | border: 1px solid $dropdownDotBlueLine;
15 | }
16 |
17 | .dropdownDot--green {
18 | background-color: $dropdownDotGreenFill;
19 | border: 1px solid $dropdownDotGreenLine;
20 | }
21 |
22 | .dropdownDot--grey {
23 | background-color: $dropdownDotGreyFill;
24 | border: 1px solid $dropdownDotGreyLine;
25 | }
26 |
27 | .dropdownDot--red {
28 | background-color: $dropdownDotRedFill;
29 | border: 1px solid $dropdownDotRedLine;
30 | }
31 |
--------------------------------------------------------------------------------
/src/framework/fieldMessage/FieldMessage.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const FieldMessage = props => (
6 |
10 | {props.message}
11 |
12 | );
13 |
14 | FieldMessage.propTypes = {
15 | dataId: PropTypes.string,
16 | message: PropTypes.string.isRequired,
17 | };
18 |
19 | export default FieldMessage;
20 |
--------------------------------------------------------------------------------
/src/framework/fieldMessage/FieldMessage.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import CommonAssertions from '../services/test/CommonAssertions';
4 | import FieldMessage from './FieldMessage.jsx';
5 |
6 | describe('FieldMessage', () => {
7 | describe('Props', () => {
8 | CommonAssertions.assertDataId(FieldMessage);
9 |
10 | describe('message', () => {
11 | it('is rendered as text', () => {
12 | const props = {
13 | message: 'test',
14 | };
15 | const testCase = TestCaseFactory.create(
16 | FieldMessage,
17 | props
18 | );
19 | expect(testCase.dom.textContent).toBe(props.message);
20 | });
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/framework/fieldMessage/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | $fieldMessage_borderColor: #e6e6ae;
3 | $fieldMessage_bgColor: $yellow;
4 | $fieldMessage_arrowSide: 6px;
5 |
6 | .fieldMessage {
7 | position: relative;
8 | font-size: 13px;
9 | line-height: 1.2;
10 | text-align: center;
11 | color: #676747;
12 | padding: 8px;
13 | margin-top: 7px;
14 | border: 1px solid $fieldMessage_borderColor;
15 | border-radius: 3px;
16 | background-color: $fieldMessage_bgColor;
17 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
18 |
19 | &:before {
20 | content: '';
21 | display: block;
22 | width: $fieldMessage_arrowSide;
23 | height: $fieldMessage_arrowSide;
24 | border: solid $fieldMessage_borderColor;
25 | background-color: $fieldMessage_bgColor;
26 | border-width: 1px 0 0 1px;
27 | position: absolute;
28 | left: 50%;
29 | top: 0;
30 | margin-left: -$fieldMessage_arrowSide;
31 | margin-top: -4px;
32 | transform: rotate(45deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/framework/filterControl/_filterControl.scss:
--------------------------------------------------------------------------------
1 |
2 | $addedFilter_backgroundColor: shade(scale-saturation($componentBackgroundColor, 60%), 3%);
3 | $addedFilter_borderColor: shade(scale-saturation($componentBackgroundColor, 100%), 14%);
4 | $addedFilter_borderRadius: 2px;
5 | $addedFilter_paddingHorizontal: 8px;
6 | $addedFilter_fontSize: 12px;
7 |
8 | @mixin filterButton {
9 | align-items: center;
10 | background: $addedFilter_backgroundColor;
11 | border: 1px solid $addedFilter_borderColor;
12 | border-radius: $addedFilter_borderRadius;
13 | color: $componentTextColor;
14 | cursor: default;
15 | display: flex;
16 | font-size: 12px;
17 | height: 26px;
18 | margin-right: 2px;
19 | padding: 0 $addedFilter_paddingHorizontal;
20 | }
21 |
22 | .filterControl {
23 | display: flex;
24 | line-height: $lineHeight;
25 | }
26 |
--------------------------------------------------------------------------------
/src/framework/filterControl/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'filterControl';
3 | @import 'filterDropdown/filterDropdown';
4 | @import 'filterDropdown/filterFormDropdown';
5 | @import 'filterDropdown/filterOptions/filterOptionList';
6 | @import 'filterItem';
7 | @import 'forms/filterForm';
8 | @import 'forms/inputFilterForm';
9 | @import 'forms/multipleSelectFilterForm';
10 | @import 'forms/searchableMultiSelectFilterForm';
11 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/FilterDropdown.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const FilterDropdown = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | FilterDropdown.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default FilterDropdown;
16 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/FilterDropdown.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import FilterDropdown from './FilterDropdown.jsx';
5 |
6 | describe('FilterDropdown', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('are rendered', () => {
10 | const props = {
11 | children: test
,
12 | };
13 |
14 | const testCase = TestCaseFactory.create(FilterDropdown, props);
15 |
16 | expect(testCase.first('#test').textContent).toBe('test');
17 | });
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/FilterDropdownButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const FilterDropdownButton = (props) => {
7 | const classes = classNames('filterDropdownButton', {
8 | 'is-filter-dropdown-open': props.isOpen,
9 | });
10 |
11 | return (
12 |
18 | );
19 | };
20 |
21 | FilterDropdownButton.propTypes = {
22 | onClick: PropTypes.func,
23 | isOpen: PropTypes.bool,
24 | };
25 |
26 | export default FilterDropdownButton;
27 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/_filterFormDropdown.scss:
--------------------------------------------------------------------------------
1 |
2 | .filterFormDropdown {
3 | position: relative;
4 | }
5 |
6 | .filterFormDropdown__form {
7 | background: white;
8 | border-radius: $addedFilter_borderRadius;
9 | border: 1px solid $addedFilter_borderColor;
10 | color: #474b4e;
11 | font-size: $addedFilter_fontSize;
12 | position: absolute;
13 | top: 0;
14 | z-index: 1;
15 | }
16 |
17 | .filterFormDropdown__form__header {
18 | background: $componentBackgroundColor;
19 | border-bottom: 1px solid #d4d8db;
20 | display: flex;
21 | font-weight: 400;
22 | justify-content: space-between;
23 | padding: 6px;
24 | white-space: nowrap;
25 | }
26 |
27 | .filterFormDropdown__form__header__title {
28 | width: 100%;
29 | }
30 |
31 | .filterFormDropdown__form__header__backButton {
32 | @include backIcon;
33 |
34 | margin-right: 2px;
35 | top: 2px;
36 | }
37 |
38 | .filterFormDropdown__form__header__closeButton {
39 | @include crossIcon($marginTop: 1px, $marginLeft: 5px);
40 |
41 | height: 10px;
42 | width: 10px;
43 | }
44 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/filterOptions/FilterOptionList.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import FilterOption from '../../../services/filter/FilterOption';
6 |
7 | const FilterOptionsList = (props) => {
8 | const filterOptionListItems =
9 | props.filterOptions.map((filterOption, index) => {
10 | const onSelectFilterOption = () =>
11 | props.onSelectFilterOption(filterOption);
12 |
13 | return (
14 |
19 | {filterOption.name}
20 |
21 | );
22 | }
23 | );
24 |
25 | return (
26 |
27 | {filterOptionListItems}
28 |
29 | );
30 | };
31 |
32 | FilterOptionsList.propTypes = {
33 | filterOptions: PropTypes.arrayOf(
34 | PropTypes.instanceOf(FilterOption)
35 | ).isRequired,
36 | onSelectFilterOption: PropTypes.func.isRequired, // eslint-disable-line react/no-unused-prop-types
37 | };
38 |
39 | export default FilterOptionsList;
40 |
--------------------------------------------------------------------------------
/src/framework/filterControl/filterDropdown/filterOptions/_filterOptionList.scss:
--------------------------------------------------------------------------------
1 |
2 | .filterOptionListItem {
3 | cursor: pointer;
4 | padding: 6px;
5 | white-space: nowrap;
6 |
7 | &:hover {
8 | background: $lightGrey;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/framework/filterControl/forms/_filterForm.scss:
--------------------------------------------------------------------------------
1 |
2 | .filterForm__buttons {
3 | padding: 6px;
4 | text-align: center;
5 | }
6 |
7 | .filterForm__buttons button {
8 | white-space: nowrap;
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/filterControl/forms/_inputFilterForm.scss:
--------------------------------------------------------------------------------
1 |
2 | .inputFilterForm {
3 | &__filterName {
4 | font-weight: 400;
5 | }
6 |
7 | &__filterName,
8 | &__filterValueWrapper {
9 | padding: 10px 6px 6px;
10 | }
11 |
12 | &__enteredValue {
13 | padding: 4px;
14 | width: 100%;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/framework/filterControl/forms/_multipleSelectFilterForm.scss:
--------------------------------------------------------------------------------
1 |
2 | .filterForm--multiSelect__checkbox {
3 | padding: 6px;
4 | white-space: nowrap;
5 |
6 | &:hover {
7 | background: $lightGrey;
8 | }
9 |
10 | .checkBox {
11 | margin-top: -2px;
12 | padding-right: 6px;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/framework/filterControl/forms/_searchableMultiSelectFilterForm.scss:
--------------------------------------------------------------------------------
1 | .filterForm__search {
2 | margin: 0.5em;
3 | }
--------------------------------------------------------------------------------
/src/framework/form/Form.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as FormPanel,
7 | } from './FormPanel.jsx';
8 |
9 | export {
10 | default as FormFooter,
11 | } from './footer/FormFooter.jsx';
12 |
13 | const Form = (props) => {
14 | let submitInput;
15 |
16 | if (props.onSubmit) {
17 | submitInput = ;
18 | }
19 |
20 | return (
21 |
30 | );
31 | };
32 |
33 | Form.propTypes = {
34 | dataId: PropTypes.string,
35 | onSubmit: PropTypes.func,
36 | children: PropTypes.any,
37 | };
38 |
39 | export default Form;
40 |
--------------------------------------------------------------------------------
/src/framework/form/FormPanel.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const FormPanel = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | FormPanel.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default FormPanel;
16 |
--------------------------------------------------------------------------------
/src/framework/form/FormPanel.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import FormPanel from './FormPanel.jsx';
4 |
5 | describe('FormPanel', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'test',
11 | };
12 | const testCase = TestCaseFactory.create(FormPanel, props);
13 | expect(testCase.dom.textContent).toBe(props.children);
14 | });
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/form/_formPanel.scss:
--------------------------------------------------------------------------------
1 |
2 | .formPanel {
3 | background-color: #f6fafc;
4 | border: 1px solid #d4d8db;
5 | padding: 30px;
6 | }
7 |
--------------------------------------------------------------------------------
/src/framework/form/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'formPanel';
3 | @import 'footer/formFooter';
4 |
--------------------------------------------------------------------------------
/src/framework/form/footer/FormFooter.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const FormFooter = (props) => {
6 | let left;
7 |
8 | if (props.left) {
9 | left = (
10 |
11 | {props.left}
12 |
13 | );
14 | }
15 |
16 | const right = (
17 |
18 | {props.right}
19 |
20 | );
21 |
22 | return (
23 |
24 | {left}
25 | {right}
26 |
27 | );
28 | };
29 |
30 | FormFooter.propTypes = {
31 | left: PropTypes.any,
32 | right: PropTypes.any.isRequired,
33 | };
34 |
35 | export default FormFooter;
36 |
--------------------------------------------------------------------------------
/src/framework/form/footer/_formFooter.scss:
--------------------------------------------------------------------------------
1 |
2 | .formFooter {
3 | @include footer;
4 | margin-top: 32px;
5 | }
6 |
7 | .formFooterSection {
8 | @include footerSection;
9 | }
10 |
11 | .formFooterSection--left {
12 | @include footerSectionLeft;
13 | }
14 |
15 | .formFooterSection--right {
16 | @include footerSectionRight;
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/src/framework/global/_blur.scss:
--------------------------------------------------------------------------------
1 | // Make content blurred, so that another UI element captures the user's attention.
2 | @mixin backgroundBlur {
3 | filter: blur(2px);
4 | }
5 |
--------------------------------------------------------------------------------
/src/framework/global/_body.scss:
--------------------------------------------------------------------------------
1 | $lineHeight: 12px;
2 | $minWidth: 504px;
3 | $maxWidth: 1200px;
4 | $viewport_normalScreen: '(max-width: 800px)';
5 | $viewport_bigScreen: '(max-width: 1200px)';
6 |
7 | body {
8 | min-width: $minWidth;
9 | overflow-x: hidden;
10 | }
11 |
--------------------------------------------------------------------------------
/src/framework/global/_button.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Inline-block solves a problem with putting an icon and text next to each
4 | * other in Firefox.
5 | * 2. Since we can't use `display: inline-flex` and `align-items: center` to
6 | * vertically align the content, we need to use padding to do it manually.
7 | */
8 | @mixin button() {
9 | @include font;
10 | appearance: none;
11 | display: inline-block; /* 1 */
12 | height: 30px;
13 | padding: 7px 11px; /* 2 */
14 | font-size: 12px;
15 | line-height: $lineHeight;
16 | font-weight: 400;
17 | opacity: 1;
18 | border-radius: 3px;
19 | border: 1px solid transparent;
20 | box-shadow: 0 0 0 rgba(0, 0, 0, 0);
21 | transition: 0.1s all ease-in-out;
22 | cursor: pointer;
23 |
24 | &:hover {
25 | opacity: 0.95;
26 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
27 | }
28 |
29 | &.is-button-disabled {
30 | pointer-events: none;
31 | opacity: 0.5;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/framework/global/_fonts.scss:
--------------------------------------------------------------------------------
1 | @import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700);
2 |
3 | @mixin font() {
4 | font-family: 'Roboto', sans-serif;
5 | }
6 |
7 | .font-roboto {
8 | @include font;
9 | font-weight: 300;
10 | -webkit-font-smoothing: antialiased;
11 | }
12 |
13 | body,
14 | input,
15 | textarea {
16 | @extend .font-roboto;
17 | color: $componentTextColor;
18 | }
19 |
--------------------------------------------------------------------------------
/src/framework/global/_footer.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin footer {
3 | display: flex;
4 | justify-content: space-between;
5 | border-top: 1px dashed #cbd3d7;
6 | padding-top: 15px;
7 | }
8 |
9 | @mixin footerSection {
10 | display: flex;
11 | flex: 1 1 auto;
12 | }
13 |
14 | /**
15 | * 1. All direct children of the left section will have space on the
16 | * right side.
17 | */
18 | @mixin footerSectionLeft {
19 | > * {
20 | margin-right: 10px; /* 1 */
21 | }
22 | }
23 |
24 | /**
25 | * 1. Align items horizontally to the right.
26 | * 2. All direct children of the right section will have space on the
27 | * left side.
28 | */
29 | @mixin footerSectionRight {
30 | justify-content: flex-end; /* 1 */
31 |
32 | > * {
33 | margin-left: 10px; /* 2 */
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/framework/global/_form.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * These mixins and variables are used by form field components, e.g. text input
4 | * and text area.
5 | */
6 |
7 | $fieldPadding: 10px;
8 |
9 | @mixin fieldFocus() {
10 | outline: none;
11 | border-color: #2ba7e8;
12 | box-shadow: inset 0 0 0 2px #2ba7e8;
13 | }
14 |
15 | @mixin field() {
16 | display: block;
17 | appearance: none;
18 | color: #444444;
19 | border: 1px solid #cccccc;
20 | font-size: 13px;
21 | font-weight: 300;
22 |
23 | &:focus {
24 | @include fieldFocus;
25 | }
26 | }
27 |
28 | @mixin fieldError() {
29 | background-color: #fffff1;
30 | border-color: #dedeb9;
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/global/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'reset';
3 | @import 'blur';
4 | @import 'body';
5 | @import 'button';
6 | @import 'colors';
7 | @import 'cssIcons';
8 | @import 'fonts';
9 | @import 'footer';
10 | @import 'form';
11 | @import 'label';
12 | @import 'list';
13 | @import 'logo';
14 | @import 'misc';
15 | @import 'modal';
16 |
--------------------------------------------------------------------------------
/src/framework/global/_label.scss:
--------------------------------------------------------------------------------
1 |
2 | // Vertically aligns label with control (top). Especially useful for the case
3 | // when field validation error is added to control, so control height gets much
4 | // bigger.
5 | $alignLabelWithFieldOffsetTop: 11px;
6 |
7 | // Default label padding-top + labeledField label height and margin.
8 | $alignLabelWithLabeledFieldOffsetTop: 26px;
9 |
--------------------------------------------------------------------------------
/src/framework/global/_list.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin listItem {
3 | padding: 10px;
4 | font-size: 13px;
5 | line-height: $lineHeight;
6 | font-weight: 300;
7 | background-color: #f6fafc;
8 | color: #526770;
9 | border-bottom: 1px solid #d4d8db;
10 | }
11 |
--------------------------------------------------------------------------------
/src/framework/global/_misc.scss:
--------------------------------------------------------------------------------
1 | a,
2 | .blueLink {
3 | text-decoration: none;
4 | color: $linkColor;
5 | }
6 |
7 | /**
8 | * 1. Addresses an issue in Firefox thats set large, implicit min-widths.
9 | * Setting min-width to 0 allows inputs to adhere to flexbox rules.
10 | */
11 | input {
12 | outline: none;
13 | min-width: 0; /* 1 */
14 | }
15 |
16 | $textInput_height: 32px;
17 |
--------------------------------------------------------------------------------
/src/framework/global/_modal.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Center modal vertically and horizontally in IE11, and allow the container
4 | * to scroll if the modal is clipped.
5 | * 2. Center modal vertically.
6 | */
7 | @mixin modal {
8 | flex: 0 0 auto; /* 1 */
9 | margin: auto; /* 2 */
10 | line-height: $lineHeight;
11 | box-shadow: 0 2px 9px rgba(0, 0, 0, 0.15);
12 | }
13 |
--------------------------------------------------------------------------------
/src/framework/glossary/_index.scss:
--------------------------------------------------------------------------------
1 | .glossaryTooltip__icon {
2 | background-color: transparent;
3 | border: none;
4 | color: inherit;
5 | display: inline-block;
6 | height: 13px;
7 | line-height: 13px;
8 | padding: 0;
9 | vertical-align: middle;
10 | width: 13px;
11 |
12 | svg {
13 | height: 100%;
14 | width: 100%;
15 | }
16 |
17 | &.icon-pos-before {
18 | margin: 0 5px 0 0;
19 | }
20 |
21 | &.icon-pos-after {
22 | margin: 0 0 0 5px;
23 | }
24 | }
25 |
26 | .glossaryTooltip__tooltip {
27 | background: white;
28 | box-shadow: 0 2px 4px 0 rgba(189, 189, 189, 0.5);
29 | font-size: 13px;
30 | padding: 15px 13px;
31 | width: 300px;
32 | z-index: 9999;
33 | }
34 |
--------------------------------------------------------------------------------
/src/framework/grid/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | $gridContainerBorder: 1px solid $componentStrokeColor;
3 |
4 | @mixin gridCellPadding {
5 | padding-left: 8px;
6 | padding-right: 8px;
7 |
8 | &:first-child {
9 | padding-left: 15px;
10 | }
11 |
12 | &:last-child {
13 | padding-right: 15px;
14 | }
15 | }
16 |
17 | @mixin gridBaseStyles {
18 | color: #474b4e;
19 | font-weight: 400;
20 | font-size: 13px;
21 | -webkit-font-smoothing: subpixel-antialiased;
22 | }
23 |
24 | @import 'controls/index';
25 | @import 'header/index';
26 | @import 'body/index';
27 | @import 'footer/index';
28 | @import 'icon/index';
29 | @import 'empty/index';
30 | @import 'loading/index';
31 | @import 'stickyGrid/index';
32 |
33 | .grid__container {
34 | border: $gridContainerBorder;
35 | margin: 0 auto;
36 | }
37 |
38 | .grid__table {
39 | @include gridBaseStyles;
40 | width: 100%;
41 | text-align: left;
42 | line-height: $lineHeight;
43 | }
44 |
--------------------------------------------------------------------------------
/src/framework/grid/body/GridBody.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const GridBody = (props) => {
7 | const {
8 | initialLoadingRow,
9 | loadingRow,
10 | emptyRow,
11 | } = props;
12 |
13 | const sectionClass = classNames(
14 | 'grid__body',
15 | props.classBody
16 | );
17 |
18 | return (
19 |
20 | {/* A row to indicate initial loading progress */}
21 | {initialLoadingRow}
22 |
23 | {/* A row to indicate empty state */}
24 | {emptyRow}
25 |
26 | {/* Recycled rows */}
27 | {props.children}
28 |
29 | {/* A row to indicate loading progress */}
30 | {loadingRow}
31 |
32 | );
33 | };
34 |
35 | GridBody.propTypes = {
36 | children: PropTypes.any,
37 | // Initial loading state
38 | initialLoadingRow: PropTypes.element,
39 | // Empty state
40 | emptyRow: PropTypes.element,
41 | // Loading state
42 | loadingRow: PropTypes.element,
43 | // Classes
44 | classBody: PropTypes.string,
45 | };
46 |
47 | export default GridBody;
48 |
--------------------------------------------------------------------------------
/src/framework/grid/body/GridBodyCell.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const GridBodyCell = (props) => {
7 | const {
8 | canOverflow,
9 | ...innerCellProps
10 | } = props.innerCellProps;
11 |
12 | const classes = classNames('gridBodyCell', props.classBodyCell);
13 | const decoratedInnerCellProps = Object.assign({}, innerCellProps, {
14 | className: classNames(
15 | 'gridBodyCellLiner',
16 | innerCellProps.className,
17 | {
18 | 'gridBodyCellLiner--overflow': canOverflow,
19 | }
20 | ),
21 | });
22 |
23 | return (
24 |
25 |
26 | |
27 | );
28 | };
29 |
30 | GridBodyCell.propTypes = {
31 | innerCellProps: PropTypes.object,
32 | // Classes
33 | classBodyCell: PropTypes.string,
34 | };
35 |
36 | GridBodyCell.defaultProps = {
37 | innerCellProps: {},
38 | };
39 |
40 | export default GridBodyCell;
41 |
--------------------------------------------------------------------------------
/src/framework/grid/body/GridBodyEditableCell.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const GridBodyEditableCell = (props) => {
6 | const onClick = props.onClick.bind(this);
7 |
8 | return (
9 |
13 |
14 | {props.children}
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | GridBodyEditableCell.propTypes = {
22 | children: PropTypes.any,
23 | onClick: PropTypes.func.isRequired,
24 | };
25 |
26 | export default GridBodyEditableCell;
27 |
--------------------------------------------------------------------------------
/src/framework/grid/body/GridBodyEditableCell.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import GridBodyEditableCell from './GridBodyEditableCell.jsx';
4 |
5 | describe('GridBodyEditableCell', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'Test',
11 | onClick: () => undefined,
12 | };
13 |
14 | const testCase = TestCaseFactory.create(
15 | GridBodyEditableCell,
16 | props
17 | );
18 |
19 | expect(testCase.dom.textContent).toBe(props.children);
20 | });
21 | });
22 |
23 | describe('onClick', () => {
24 | it('is executed on click', () => {
25 | const props = {
26 | onClick: jasmine.createSpy('onClick'),
27 | };
28 |
29 | const testCase = TestCaseFactory.create(
30 | GridBodyEditableCell,
31 | props
32 | );
33 |
34 | expect(props.onClick).not.toHaveBeenCalled();
35 | testCase.trigger('click');
36 | expect(props.onClick).toHaveBeenCalled();
37 | });
38 | });
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/src/framework/grid/body/GridFakeRow.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const GridFakeRow = props => (
6 |
7 | |
8 |
9 | |
10 |
11 | );
12 |
13 | GridFakeRow.propTypes = {
14 | columnsCount: PropTypes.number.isRequired,
15 | style: PropTypes.object,
16 | };
17 |
18 | export default GridFakeRow;
19 |
--------------------------------------------------------------------------------
/src/framework/grid/body/_gridBody.scss:
--------------------------------------------------------------------------------
1 | .grid__body {
2 | background: #ffffff;
3 | }
4 |
--------------------------------------------------------------------------------
/src/framework/grid/body/_gridRow.scss:
--------------------------------------------------------------------------------
1 |
2 | .gridRow--even {
3 | background-color: transparent;
4 | }
5 |
6 | .gridRow--odd {
7 | background-color: #f9f9f9;
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/grid/body/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'gridBody';
2 | @import 'gridRow';
3 | @import 'gridBodyCell';
4 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/GridControls.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const GridControls = props => (
6 |
7 |
8 | {props.children}
9 |
10 |
11 | );
12 |
13 | GridControls.propTypes = {
14 | children: PropTypes.any,
15 | };
16 |
17 | export default GridControls;
18 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/GridControls.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import GridControls from './GridControls.jsx';
5 |
6 | describe('GridControls', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('are rendered', () => {
10 | const props = {
11 | children: ,
12 | };
13 |
14 | const testCase = TestCaseFactory.create(GridControls, props);
15 | expect(testCase.first('.test')).toBeDefined();
16 | });
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/_gridControls.scss:
--------------------------------------------------------------------------------
1 | .grid__controls {
2 | display: flex;
3 | justify-content: center;
4 | height: 40px;
5 | }
6 |
7 | .grid__controls__liner {
8 | flex: 1;
9 | max-width: $maxWidth;
10 | height: inherit;
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | padding: 0 15px;
15 | border: solid $componentStrokeColor;
16 | border-width: 1px 1px 0;
17 | background: $componentBackgroundColor;
18 | }
19 |
20 | .fixedThead {
21 | .grid__controls {
22 | position: fixed;
23 | top: 0;
24 | width: 100%;
25 | z-index: 5;
26 | }
27 |
28 | .grid__controls__placeholder {
29 | height: 71px;
30 | max-width: $maxWidth;
31 | margin: 0 auto;
32 | background: $componentBackgroundColor;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'gridControls';
2 |
3 | @import 'batch/index';
4 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/batch/_batch.scss:
--------------------------------------------------------------------------------
1 | .grid__batch {
2 | vertical-align: middle;
3 | }
4 |
5 | .grid__batch__button {
6 | display: inline-block;
7 | color: shade(scale-saturation($componentTextColor, 100%), 100%);
8 | border: 1px dashed scale-saturation($componentTextColor, 22%);
9 | border-radius: 2px;
10 | font-size: 11px;
11 | opacity: 0.4;
12 | padding: 5px 17px 5px 8px;
13 | white-space: nowrap;
14 |
15 | .icon {
16 | width: 12px;
17 | float: left;
18 | height: 10px;
19 | opacity: 0.7;
20 | font-size: 10px;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/framework/grid/controls/batch/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'batch';
2 |
--------------------------------------------------------------------------------
/src/framework/grid/empty/GridEmptyRow.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const GridEmptyRow = (props) => {
6 | const style = {
7 | height: props.height,
8 | };
9 | const message = props.message || 'No data.';
10 |
11 | return (
12 |
13 | |
14 |
18 | {message}
19 |
20 | |
21 |
22 | );
23 | };
24 |
25 | GridEmptyRow.propTypes = {
26 | columnsCount: PropTypes.number.isRequired,
27 | height: PropTypes.number,
28 | message: PropTypes.any,
29 | };
30 |
31 | export default GridEmptyRow;
32 |
--------------------------------------------------------------------------------
/src/framework/grid/empty/_gridEmptyRow.scss:
--------------------------------------------------------------------------------
1 |
2 | .gridEmptyRow {
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | height: 100%;
7 | }
8 |
--------------------------------------------------------------------------------
/src/framework/grid/empty/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'gridEmptyRow';
3 |
--------------------------------------------------------------------------------
/src/framework/grid/footer/GridFooterCell.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const GridFooterCell = (props) => {
7 | const classes = classNames('grid__footer__cell', props.classFooterCell, {
8 | 'grid__footer__cell--divider': props.innerCellProps.children,
9 | 'grid__footer__cell--firstDivider': props.isFirstCellWithChildren,
10 | });
11 | const decoratedInnerCellProps = Object.assign({}, props.innerCellProps, {
12 | className: classNames(
13 | 'grid__footer__cell__liner',
14 | props.innerCellProps.className
15 | ),
16 | });
17 |
18 | return (
19 |
20 |
21 | |
22 | );
23 | };
24 |
25 | GridFooterCell.propTypes = {
26 | classFooterCell: PropTypes.string,
27 | innerCellProps: PropTypes.object,
28 | isFirstCellWithChildren: PropTypes.bool,
29 | };
30 |
31 | GridFooterCell.defaultProps = {
32 | innerCellProps: {},
33 | };
34 |
35 | export default GridFooterCell;
36 |
--------------------------------------------------------------------------------
/src/framework/grid/footer/_gridFooter.scss:
--------------------------------------------------------------------------------
1 |
2 | .grid__footer--sticky {
3 | bottom: 0;
4 | border-left: 1px solid $componentStrokeColor;
5 | border-right: 1px solid $componentStrokeColor;
6 | margin-left: -1px;
7 | position: fixed;
8 | z-index: 10;
9 | }
10 |
11 | .grid__footer__cell {
12 | @include gridCellPadding;
13 | background: $componentBackgroundColor;
14 | border-top: 1px solid $componentStrokeColor;
15 | height: 31px;
16 | vertical-align: middle;
17 | }
18 |
19 | .grid__footer__cell--divider {
20 | border-right: 1px solid $componentStrokeColor;
21 | }
22 |
23 | .grid__footer__cell--firstDivider {
24 | border-left: 1px solid $componentStrokeColor;
25 | }
26 |
27 | .grid__footer__cell__liner {
28 | align-items: center;
29 | display: flex;
30 | height: 100%;
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/grid/footer/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'gridFooter';
2 |
--------------------------------------------------------------------------------
/src/framework/grid/header/GridHeader.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import GridHeaderCell from './GridHeaderCell.jsx';
7 |
8 | const GridHeader = (props) => {
9 | const sectionClass = classNames('grid__header', props.classHeader);
10 | const rowClass = classNames('grid__header__row', props.classHeaderRow);
11 |
12 | // Create cells.
13 | const headerCells =
14 | props.headerCellPropsProviders.map((cellPropsProvider, index) => (
15 |
20 | )
21 | );
22 |
23 | return (
24 |
25 |
26 | {headerCells}
27 |
28 |
29 | );
30 | };
31 |
32 | GridHeader.propTypes = {
33 | headerCellPropsProviders: PropTypes.array.isRequired,
34 | // Classes
35 | classHeader: PropTypes.string,
36 | classHeaderRow: PropTypes.string,
37 | classHeaderCell: GridHeaderCell.propTypes.classHeaderCell,
38 | };
39 |
40 | export default GridHeader;
41 |
--------------------------------------------------------------------------------
/src/framework/grid/header/GridHeaderCell.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const GridHeaderCell = (props) => {
7 | // Cell classes.
8 | const classes = classNames('grid__header__cell', props.classHeaderCell);
9 |
10 | // We want to add on our own classes to the inner cell, without destroying
11 | // any classes that have been provided.
12 | const decoratedInnerCellProps = Object.assign({}, props.innerCellProps, {
13 | className: classNames(
14 | 'grid__header__cellLiner',
15 | props.innerCellProps.className
16 | ),
17 | });
18 |
19 | return (
20 |
21 |
22 | |
23 | );
24 | };
25 |
26 | GridHeaderCell.propTypes = {
27 | innerCellProps: PropTypes.object,
28 | // Classes
29 | classHeaderCell: PropTypes.string,
30 | };
31 |
32 | GridHeaderCell.defaultProps = {
33 | innerCellProps: {},
34 | };
35 |
36 | export default GridHeaderCell;
37 |
--------------------------------------------------------------------------------
/src/framework/grid/header/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'gridHeader';
2 |
--------------------------------------------------------------------------------
/src/framework/grid/icon/GridIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const GridIcon = (props) => {
8 | const typeToIconClassMap = {
9 | [GridIcon.TYPE.EDIT]: 'icon-cog',
10 | [GridIcon.TYPE.OPTIONS]: 'icon-ellipsis',
11 | };
12 |
13 | const classes = classNames(
14 | 'gridIcon',
15 | typeToIconClassMap[props.type],
16 | {
17 | icon: props.type,
18 | }
19 | );
20 |
21 | function onClick(event) {
22 | if (props.onClick) {
23 | props.onClick(props.data, event);
24 | }
25 | }
26 |
27 | return (
28 |
32 | );
33 | };
34 |
35 | GridIcon.TYPE = keyMirror({
36 | EDIT: null,
37 | OPTIONS: null,
38 | });
39 |
40 | GridIcon.propTypes = {
41 | type: PropTypes.string,
42 | onClick: PropTypes.func,
43 | data: PropTypes.any,
44 | };
45 |
46 | export default GridIcon;
47 |
--------------------------------------------------------------------------------
/src/framework/grid/icon/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | $gridIconSize: 14px;
3 |
4 | /**
5 | * 1. Minor vertical alignment adjustment within grid rows.
6 | */
7 | .gridIcon {
8 | display: inline-block;
9 | width: $gridIconSize;
10 | height: $gridIconSize;
11 | vertical-align: top; /* 1 */
12 | opacity: 0.6;
13 | cursor: pointer;
14 |
15 | &:hover {
16 | opacity: 0.25;
17 | }
18 |
19 | & + & {
20 | margin-left: 15px;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/framework/grid/loading/GridLoadingRow.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import Progress from '../../progress/Progress.jsx';
7 |
8 | const GridLoadingRow = (props) => {
9 | const className = classNames(
10 | 'gridLoadingRow',
11 | props.isInitial ? 'gridLoadingRow--initial' : null
12 | );
13 |
14 | return (
15 |
16 | |
17 |
20 | |
21 |
22 | );
23 | };
24 |
25 | GridLoadingRow.propTypes = {
26 | columnsCount: PropTypes.number.isRequired,
27 | isInitial: PropTypes.bool,
28 | };
29 |
30 | export default GridLoadingRow;
31 |
--------------------------------------------------------------------------------
/src/framework/grid/loading/_gridLoadingRow.scss:
--------------------------------------------------------------------------------
1 |
2 | .gridLoadingRow {
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | height: 68px;
7 | }
8 |
9 | /**
10 | * 1. Optically center the progress component.
11 | */
12 | .gridLoadingRow--initial {
13 | height: 400px;
14 | padding-bottom: 30px; /* 1 */
15 | }
16 |
--------------------------------------------------------------------------------
/src/framework/grid/loading/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'gridLoadingRow';
2 |
--------------------------------------------------------------------------------
/src/framework/grid/stickyGrid/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'stickyGrid';
2 |
--------------------------------------------------------------------------------
/src/framework/grid/stickyGrid/_stickyGrid.scss:
--------------------------------------------------------------------------------
1 |
2 | .stickyGrid {
3 | position: relative;
4 | }
5 |
6 | /**
7 | * This header is hidden until the user scrolls down far enough for the
8 | * grid header to become stuck.
9 | *
10 | * 1. Prevent cells from overflowing the header.
11 | */
12 | .stickyGridHeader {
13 | @include gridBaseStyles;
14 | background: $componentBackgroundColor;
15 | border: $gridContainerBorder;
16 | border-bottom: 1px solid $componentStrokeColor;
17 | border-top: 0;
18 | display: none;
19 | height: 31px;
20 | line-height: 30px;
21 | overflow: hidden; /* 1 */
22 | position: fixed;
23 | top: 0;
24 | z-index: 10;
25 |
26 | /**
27 | * We stick the header with display instead of position, to avoid incurring
28 | * a reflow and repaint.
29 | */
30 | .is-grid-header-stuck > & {
31 | align-items: center;
32 | display: flex;
33 | justify-content: space-between;
34 | }
35 | }
36 |
37 | .stickyGridHeaderCell {
38 | @include gridCellPadding;
39 | height: 100%;
40 | overflow: hidden;
41 | text-overflow: ellipsis;
42 | white-space: nowrap;
43 | }
44 |
--------------------------------------------------------------------------------
/src/framework/horizontalLine/HorizontalLine.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | export default () => (
5 |
6 | );
7 |
--------------------------------------------------------------------------------
/src/framework/horizontalLine/HorizontalLine.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import HorizontalLine from './HorizontalLine.jsx';
4 |
5 | describe('HorizontalLine', () => {
6 | it('is rendered with appropriate class', () => {
7 | const testCase = TestCaseFactory.create(HorizontalLine, {});
8 | expect(testCase.dom.className).toBe('horizontalLine');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/framework/horizontalLine/_horizontalLine.scss:
--------------------------------------------------------------------------------
1 | .horizontalLine {
2 | border: 0;
3 | border-top: 1px solid #ccd3d5;
4 | }
5 |
--------------------------------------------------------------------------------
/src/framework/horizontalLine/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'horizontalLine';
2 |
--------------------------------------------------------------------------------
/src/framework/imageUpload/ImagePreview.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React, {
3 | Component,
4 | } from 'react';
5 |
6 | import PropTypes from 'prop-types';
7 |
8 | class ImagePreview extends Component {
9 | componentDidUpdate(prevProps) {
10 | if (this.props.imageBinaryUrl === prevProps.imageBinaryUrl) {
11 | return null;
12 | }
13 | const image = new Image();
14 |
15 | image.onload = () => {
16 | this.props.hasLoaded(image);
17 | };
18 |
19 | image.src = this.props.imageBinaryUrl;
20 | }
21 |
22 | render() {
23 | const image = this.props.imageBinaryUrl ?
24 | this.props.imageBinaryUrl :
25 | this.props.children;
26 | return (
);
27 | }
28 | }
29 |
30 | ImagePreview.propTypes = {
31 | children: PropTypes.string,
32 | hasLoaded: PropTypes.func,
33 | imageBinaryUrl: PropTypes.any,
34 | };
35 |
36 | export default ImagePreview;
37 |
--------------------------------------------------------------------------------
/src/framework/imageUpload/_imageUpload.scss:
--------------------------------------------------------------------------------
1 |
2 | .imageUpload {
3 | display: table;
4 | position: relative;
5 |
6 | &--hidden {
7 | display: none;
8 | }
9 |
10 | &__closeButton {
11 | @include crossIcon($marginTop: 4px, $marginLeft: 9px);
12 |
13 | $closeButtonSide: 20px;
14 | background-image: linear-gradient(#e9f0f2, #e2ebee);
15 | border-radius: 2px;
16 | border: 1px solid #bbced6;
17 | color: #517582;
18 | display: block;
19 | height: $closeButtonSide;
20 | opacity: 0.7;
21 | position: absolute;
22 | right: 0;
23 | top: 0;
24 | width: $closeButtonSide;
25 |
26 | &:before,
27 | &:after {
28 | background-color: #517582;
29 | }
30 |
31 | &:hover {
32 | opacity: 1;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/framework/imageUpload/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'imageUpload';
3 |
--------------------------------------------------------------------------------
/src/framework/kpi/Kpi.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const Kpi = (props) => {
7 | const className = classNames('kpi', props.className);
8 |
9 | return (
10 |
14 | {props.children}
15 |
16 | );
17 | };
18 |
19 | Kpi.propTypes = {
20 | children: PropTypes.any,
21 | className: PropTypes.string,
22 | title: PropTypes.string,
23 | };
24 |
25 | export default Kpi;
26 |
--------------------------------------------------------------------------------
/src/framework/kpi/KpiNegative.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import Kpi from './Kpi.jsx';
4 |
5 | const KpiNegative = props => (
6 |
10 | {props.children}
11 |
12 | );
13 |
14 | KpiNegative.propTypes = Object.assign({}, Kpi.propTypes);
15 |
16 | export default KpiNegative;
17 |
--------------------------------------------------------------------------------
/src/framework/kpi/KpiNegative.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import KpiNegative from './KpiNegative.jsx';
5 |
6 | describe('KpiNegative', () => {
7 | describe('Props', () => {
8 | describe('title', () => {
9 | it('is applied to element as an attribute', () => {
10 | const props = {
11 | title: 'test',
12 | };
13 | const testCase = TestCaseFactory.create(KpiNegative, props);
14 | expect(testCase.dom.getAttribute('title')).toContain(props.title);
15 | });
16 | });
17 |
18 | describe('children', () => {
19 | it('are rendered', () => {
20 | const props = {
21 | children: ,
22 | };
23 | const testCase = TestCaseFactory.create(KpiNegative, props);
24 | expect(testCase.first('#test')).toBeDefined();
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/kpi/KpiPositive.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import Kpi from './Kpi.jsx';
4 |
5 | const KpiPositive = props => (
6 |
10 | {props.children}
11 |
12 | );
13 |
14 | KpiPositive.propTypes = Object.assign({}, Kpi.propTypes);
15 |
16 | export default KpiPositive;
17 |
--------------------------------------------------------------------------------
/src/framework/kpi/KpiPositive.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import KpiPositive from './KpiPositive.jsx';
5 |
6 | describe('KpiPositive', () => {
7 | describe('Props', () => {
8 | describe('title', () => {
9 | it('is applied to element as an attribute', () => {
10 | const props = {
11 | title: 'test',
12 | };
13 | const testCase = TestCaseFactory.create(KpiPositive, props);
14 | expect(testCase.dom.getAttribute('title')).toContain(props.title);
15 | });
16 | });
17 |
18 | describe('children', () => {
19 | it('are rendered', () => {
20 | const props = {
21 | children: ,
22 | };
23 | const testCase = TestCaseFactory.create(KpiPositive, props);
24 | expect(testCase.first('#test')).toBeDefined();
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/framework/kpi/_index.scss:
--------------------------------------------------------------------------------
1 | .kpi {
2 | font-size: 12px;
3 | padding-left: 2px;
4 | font-weight: 400;
5 | }
6 |
7 | .kpi--positive {
8 | color: $green;
9 | }
10 |
11 | .kpi--negative {
12 | color: $red;
13 | }
14 |
--------------------------------------------------------------------------------
/src/framework/kpi/index.jsx:
--------------------------------------------------------------------------------
1 | export {
2 | default as Kpi,
3 | } from './Kpi.jsx';
4 |
5 | export {
6 | default as KpiPositive,
7 | } from './KpiPositive.jsx';
8 |
9 | export {
10 | default as KpiNegative,
11 | } from './KpiNegative.jsx';
12 |
--------------------------------------------------------------------------------
/src/framework/label/SubLabel.jsx:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const SubLabel = (props) => {
6 | const classes = classNames('subLabel', props.className);
7 |
8 | return (
9 |
10 | {props.children}
11 |
12 | );
13 | };
14 |
15 | SubLabel.propTypes = {
16 | children: PropTypes.any,
17 | className: PropTypes.string,
18 | };
19 |
20 | export default SubLabel;
21 |
--------------------------------------------------------------------------------
/src/framework/label/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'label';
2 | @import 'subLabel';
3 |
--------------------------------------------------------------------------------
/src/framework/label/_label.scss:
--------------------------------------------------------------------------------
1 | .label {
2 | display: block;
3 | color: #526770;
4 | font-size: 13px;
5 | font-weight: 300;
6 | line-height: $lineHeight;
7 | }
8 |
9 | .label--clickable {
10 | cursor: pointer;
11 | }
12 |
13 | .label--alignedWthField {
14 | margin-top: $alignLabelWithFieldOffsetTop;
15 | }
16 |
17 | .label--alignedWithLabeledField {
18 | margin-top: $alignLabelWithLabeledFieldOffsetTop;
19 | }
20 |
--------------------------------------------------------------------------------
/src/framework/label/_subLabel.scss:
--------------------------------------------------------------------------------
1 | .subLabel {
2 | color: #7a898e;
3 | font-size: 12px;
4 | font-weight: 300;
5 | line-height: $lineHeight;
6 | }
7 |
--------------------------------------------------------------------------------
/src/framework/labeledField/LabeledField.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Label from '../label/Label.jsx';
6 |
7 | const LabeledField = props => (
8 |
9 |
10 |
13 |
14 | {props.children}
15 |
16 | );
17 |
18 | LabeledField.propTypes = {
19 | children: PropTypes.any,
20 | label: PropTypes.string,
21 | };
22 |
23 | export default LabeledField;
24 |
--------------------------------------------------------------------------------
/src/framework/labeledField/LabeledField.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import LabeledField from './LabeledField.jsx';
5 |
6 | describe('LabeledField', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('are rendered', () => {
10 | const props = {
11 | label: 'Test',
12 | children: ,
13 | };
14 |
15 | const testCase =
16 | TestCaseFactory.create(LabeledField, props);
17 | expect(testCase.first('#test')).toBeDefined();
18 | });
19 | });
20 |
21 | describe('label', () => {
22 | it('becomes the textContent of the label element', () => {
23 | const props = {
24 | label: 'Test',
25 | };
26 |
27 | const testCase =
28 | TestCaseFactory.create(LabeledField, props);
29 | expect(testCase.first('label').textContent).toBe(props.label);
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/src/framework/labeledField/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'labeledField';
2 |
--------------------------------------------------------------------------------
/src/framework/labeledField/_labeledField.scss:
--------------------------------------------------------------------------------
1 | .labeledField__label {
2 | line-height: $lineHeight;
3 | margin-bottom: 3px;
4 | }
5 |
--------------------------------------------------------------------------------
/src/framework/leftFixedLayout/LeftFixedLayout.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import LeftFixedLayout from './LeftFixedLayout.jsx';
4 |
5 | describe('LeftFixedLayout', () => {
6 | describe('Props', () => {
7 | describe('left', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | left: 'test',
11 | };
12 |
13 | const testCase = TestCaseFactory.create(LeftFixedLayout, props);
14 | expect(testCase.dom.textContent).toBe(props.left);
15 | });
16 | });
17 |
18 | describe('right', () => {
19 | it('is rendered', () => {
20 | const props = {
21 | right: 'test',
22 | };
23 |
24 | const testCase = TestCaseFactory.create(LeftFixedLayout, props);
25 | expect(testCase.dom.textContent).toBe(props.right);
26 | });
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/framework/leftFixedLayout/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'leftFixedLayout';
2 |
--------------------------------------------------------------------------------
/src/framework/leftFixedLayout/_leftFixedLayout.scss:
--------------------------------------------------------------------------------
1 |
2 | .leftFixedLayout {
3 | position: relative;
4 | }
5 |
6 | .leftFixedLayout__leftSide {
7 | left: 0;
8 | max-height: 100vh;
9 | overflow: auto;
10 | position: absolute;
11 | top: 0;
12 | width: 710px;
13 |
14 | &.leftFixedLayout__leftSide--sticky {
15 | left: auto;
16 | position: fixed;
17 | }
18 |
19 | @media #{$viewport_bigScreen} {
20 | width: 555px;
21 | }
22 | }
23 |
24 | .leftFixedLayout__rightSide {
25 | margin-left: 710px;
26 | padding-left: 10px;
27 |
28 | @media #{$viewport_bigScreen} {
29 | margin-left: 555px;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/link/Link.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const Link = (props) => {
6 | function onClick() {
7 | if (props.onClick) {
8 | props.onClick(props.data);
9 | }
10 | }
11 |
12 | return (
13 |
20 | {props.children}
21 |
22 | );
23 | };
24 |
25 | Link.propTypes = {
26 | dataId: PropTypes.string,
27 | children: PropTypes.any,
28 | href: PropTypes.string,
29 | onClick: PropTypes.func,
30 | data: PropTypes.any,
31 | target: PropTypes.string,
32 | };
33 |
34 | export default Link;
35 |
--------------------------------------------------------------------------------
/src/framework/link/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'link';
3 |
--------------------------------------------------------------------------------
/src/framework/link/_link.scss:
--------------------------------------------------------------------------------
1 | .link {
2 | display: inline-block;
3 | cursor: pointer;
4 | text-decoration: none;
5 |
6 | &:hover {
7 | text-decoration: underline;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/menu/Menu.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as MenuItem,
7 | } from './MenuItem.jsx';
8 |
9 | const Menu = props => (
10 |
11 | {props.children}
12 |
13 | );
14 |
15 | Menu.propTypes = {
16 | dataId: PropTypes.string,
17 | children: PropTypes.any,
18 | };
19 |
20 | export default Menu;
21 |
--------------------------------------------------------------------------------
/src/framework/menu/Menu.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import CommonAssertions from '../services/test/CommonAssertions';
4 | import Menu from './Menu.jsx';
5 |
6 | describe('Menu', () => {
7 | describe('Props', () => {
8 | CommonAssertions.assertDataId(Menu);
9 |
10 | describe('children', () => {
11 | it('is rendered', () => {
12 | const props = {
13 | children: 'test',
14 | };
15 | const testCase = TestCaseFactory.create(Menu, props);
16 | expect(testCase.dom.textContent).toBe(props.children);
17 | });
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/framework/menu/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'menuItem';
3 |
--------------------------------------------------------------------------------
/src/framework/modal/ModalBody.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ModalBody = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | ModalBody.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default ModalBody;
16 |
--------------------------------------------------------------------------------
/src/framework/modal/ModalBody.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import ModalBody from './ModalBody.jsx';
5 |
6 | describe('ModalBody', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered when an array', () => {
10 | const props = {
11 | children: [
12 | item1
,
13 | item2
,
14 | ],
15 | };
16 | const testCase = TestCaseFactory.create(ModalBody, props);
17 | expect(testCase.dom.children[0].textContent).toBe('item1');
18 | expect(testCase.dom.children[1].textContent).toBe('item2');
19 | });
20 |
21 | it('is rendered when a single element', () => {
22 | const props = {
23 | children: item1
,
24 | };
25 | const testCase = TestCaseFactory.create(ModalBody, props);
26 | expect(testCase.dom.children[0].textContent).toBe('item1');
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/framework/modal/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'modal';
3 | @import 'confirmation/modalConfirmation';
4 | @import 'footer/modalFooter';
5 | @import 'header/modalHeader';
6 | @import 'overlay/modalOverlay';
7 | @import 'stack/modalStack';
8 |
--------------------------------------------------------------------------------
/src/framework/modal/_modal.scss:
--------------------------------------------------------------------------------
1 |
2 | $modalPadding: 20px;
3 |
4 | .modal {
5 | @include modal;
6 | border: 4px solid white;
7 | background: #f7fbfc;
8 | border-radius: 1px;
9 | color: #526770;
10 |
11 | &.is-modal-stacked {
12 | border-color: transparent;
13 | }
14 |
15 | /**
16 | * 1. Allow user to click on modal behind top-most modal in the stack.
17 | */
18 | &.is-modal-next-in-stack {
19 | cursor: pointer; /* 1 */
20 | }
21 |
22 | /**
23 | * 1. Prevent modals farther down in the stack from being interacted with.
24 | */
25 | &.is-modal-buried-in-stack {
26 | pointer-events: none; /* 1 */
27 | }
28 | }
29 |
30 | .modalBody {
31 | padding: 0 $modalPadding;
32 | }
33 |
--------------------------------------------------------------------------------
/src/framework/modal/confirmation/ModalConfirmationBody.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ModalConfirmationBody = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | ModalConfirmationBody.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default ModalConfirmationBody;
16 |
--------------------------------------------------------------------------------
/src/framework/modal/confirmation/ModalConfirmationBody.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ModalConfirmationBody from './ModalConfirmationBody.jsx';
4 |
5 | describe('ModalConfirmationBody', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered when a string', () => {
9 | const props = {
10 | children: 'testChildren',
11 | };
12 | const testCase =
13 | TestCaseFactory.create(ModalConfirmationBody, props);
14 | expect(testCase.dom.textContent).toBe(props.children);
15 | });
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/framework/modal/confirmation/ModalConfirmationFooter.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ModalConfirmationFooter = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | ModalConfirmationFooter.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default ModalConfirmationFooter;
16 |
--------------------------------------------------------------------------------
/src/framework/modal/confirmation/ModalConfirmationFooter.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import ModalConfirmationFooter from './ModalConfirmationFooter.jsx';
5 |
6 | describe('ModalConfirmationFooter', () => {
7 | describe('Props', () => {
8 | describe('children', () => {
9 | it('is rendered when an array', () => {
10 | const props = {
11 | children: [
12 | item1
,
13 | item2
,
14 | ],
15 | };
16 | const testCase =
17 | TestCaseFactory.create(ModalConfirmationFooter, props);
18 | expect(testCase.dom.children[0].textContent).toBe('item1');
19 | expect(testCase.dom.children[1].textContent).toBe('item2');
20 | });
21 |
22 | it('is rendered when a single element', () => {
23 | const props = {
24 | children: item1
,
25 | };
26 | const testCase =
27 | TestCaseFactory.create(ModalConfirmationFooter, props);
28 | expect(testCase.dom.children[0].textContent).toBe('item1');
29 | });
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/framework/modal/confirmation/_modalConfirmation.scss:
--------------------------------------------------------------------------------
1 | .modalConfirmationBody {
2 | padding: 30px $modalPadding 15px;
3 | font-size: 16px;
4 | line-height: 20px;
5 | text-align: center;
6 | }
7 |
8 | /**
9 | * 1. Space children apart.
10 | */
11 | .modalConfirmationFooter {
12 | display: flex;
13 | justify-content: center;
14 | margin-top: 10px;
15 | padding: 0 $modalPadding $modalPadding;
16 |
17 | > * {
18 | margin: 0 5px; /* 1 */
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/framework/modal/footer/ModalFooter.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ModalFooter = (props) => {
6 | let left;
7 |
8 | if (props.left) {
9 | left = (
10 |
11 | {props.left}
12 |
13 | );
14 | }
15 |
16 | const right = (
17 |
18 | {props.right}
19 |
20 | );
21 |
22 | return (
23 |
24 |
25 | {left}
26 | {right}
27 |
28 |
29 | );
30 | };
31 |
32 | ModalFooter.propTypes = {
33 | left: PropTypes.any,
34 | right: PropTypes.any.isRequired,
35 | };
36 | export default ModalFooter;
37 |
--------------------------------------------------------------------------------
/src/framework/modal/footer/_modalFooter.scss:
--------------------------------------------------------------------------------
1 |
2 | .modalFooterContainer {
3 | padding: 0 $modalPadding $modalPadding;
4 | }
5 |
6 | .modalFooter {
7 | @include footer;
8 | margin-top: 10px;
9 | }
10 |
11 | .modalFooterSection {
12 | @include footerSection;
13 | }
14 |
15 | .modalFooterSection--left {
16 | @include footerSectionLeft;
17 | }
18 |
19 | .modalFooterSection--right {
20 | @include footerSectionRight;
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/src/framework/modal/header/ModalCloseButton.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const ModalCloseButton = props => (
6 |
10 | );
11 |
12 | ModalCloseButton.propTypes = {
13 | onClick: PropTypes.func,
14 | };
15 |
16 | export default ModalCloseButton;
17 |
--------------------------------------------------------------------------------
/src/framework/modal/header/ModalCloseButton.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ModalCloseButton from './ModalCloseButton.jsx';
4 |
5 | describe('ModalCloseButton', () => {
6 | describe('Props', () => {
7 | describe('onClick', () => {
8 | let onClick;
9 |
10 | beforeEach(() => {
11 | onClick = jasmine.createSpy('onClick');
12 |
13 | const testCase =
14 | TestCaseFactory.create(ModalCloseButton, { onClick });
15 |
16 | testCase.trigger('click');
17 | });
18 |
19 | it('is called', () => {
20 | expect(onClick).toHaveBeenCalled();
21 | });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/framework/modal/overlay/_modalOverlay.scss:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | &.is-modal-overlay-open {
4 | /* For this to work as intended modal overlay element must be outside .app */
5 | // ui-framework' guide
6 | .app,
7 | // buyer-tools
8 | .body {
9 | @include backgroundBlur;
10 | }
11 | }
12 | }
13 |
14 | .modalOverlay {
15 | /**
16 | * 1. Center modal vertically and horizontally in IE11, and allow the container
17 | * to scroll if the modal is clipped.
18 | */
19 | display: flex; /* 1 */
20 | flex-direction: column; /* 1 */
21 | align-items: center; /* 1 */
22 | position: fixed;
23 | top: 0;
24 | left: 0;
25 | right: 0;
26 | bottom: 0;
27 | background-color: rgba(black, 0.15);
28 | z-index: 100;
29 | overflow-y: auto;
30 | padding: 10px;
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/modal/stack/ModalStack.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const ModalStack = (props) => {
7 | const modalCount = props.children.length;
8 | const stackedModals = props.children.map((modal, index) => {
9 | const depth = modalCount - index;
10 | const isDepthMax = depth > 3;
11 | const stackedModalClasses = classNames(
12 | 'stackedModal',
13 | isDepthMax ? 'stackedModal--depthMax' : `stackedModal--depth${depth}`
14 | );
15 |
16 | return (
17 |
21 | {modal}
22 |
23 | );
24 | });
25 |
26 | return (
27 |
28 | {stackedModals}
29 |
30 | );
31 | };
32 |
33 | ModalStack.propTypes = {
34 | children: PropTypes.any,
35 | };
36 |
37 | export default ModalStack;
38 |
--------------------------------------------------------------------------------
/src/framework/modal/stack/_modalStack.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Center modal vertically and horizontally in IE11, and allow the container
4 | * to scroll if the modal is clipped.
5 | * 2. Center modal vertically.
6 | */
7 | .modalStack {
8 | flex: 0 0 auto; /* 1 */
9 | margin: auto; /* 2 */
10 | padding-bottom: 10px;
11 | }
12 |
13 | /**
14 | * 1. We need to form a stacking context so that the first modal isn't hidden
15 | * behind the other modals.
16 | */
17 | .stackedModal--depth1 {
18 | transform: scale(1, 1); /* 1 */
19 | }
20 |
21 | .stackedModal--depth2 {
22 | margin-bottom: -15px;
23 | transform: scale(0.9, 0.9);
24 |
25 | // Hide content of hidden modals except their header
26 | .modal > :not(.modalHeader) {
27 | display: none;
28 | }
29 | }
30 |
31 | .stackedModal--depth3 {
32 | margin-bottom: -45px;
33 | transform: scale(0.8, 0.8);
34 |
35 | // Hide content of hidden modals except their header
36 | .modal > :not(.modalHeader) {
37 | display: none;
38 | }
39 | }
40 |
41 | .stackedModal--depthMax {
42 | display: none;
43 | }
44 |
--------------------------------------------------------------------------------
/src/framework/organizationSwitcher/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'organizationSwitcher';
2 | @import 'organizationSwitcherItem';
3 |
--------------------------------------------------------------------------------
/src/framework/organizationSwitcher/_organizationSwitcherItem.scss:
--------------------------------------------------------------------------------
1 |
2 | .organizationSwitcherItem {
3 | display: flex;
4 | justify-content: space-between;
5 | align-items: center;
6 | height: 32px;
7 | padding: 0 10px;
8 | line-height: $lineHeight;
9 | background-color: #fafdfe;
10 | border: solid #d1d8da;
11 | border-width: 1px 0;
12 |
13 | &:first-child {
14 | border-top: 0;
15 | }
16 |
17 | &:last-child {
18 | border-bottom: 0;
19 | }
20 |
21 | & + & {
22 | border-top: 0;
23 | }
24 | }
25 |
26 | .organizationSwitcherItem__name {
27 | flex: 2 1 0%;
28 | font-weight: 700;
29 | overflow: hidden;
30 | white-space: nowrap;
31 | text-overflow: ellipsis;
32 | }
33 |
34 | .organizationSwitcherItem__id {
35 | flex: 1 1 0%;
36 | padding: 0 10px;
37 | overflow: hidden;
38 | white-space: nowrap;
39 | text-overflow: ellipsis;
40 | }
41 |
42 | .organizationSwitcherItem__switch {
43 | flex: 1 1 0%;
44 | font-size: 11px;
45 | font-weight: 700;
46 | text-transform: uppercase;
47 | text-align: right;
48 | color: $blue;
49 | }
50 |
51 | .organizationSwitcherItem__switchButton {
52 | cursor: pointer;
53 | }
54 |
--------------------------------------------------------------------------------
/src/framework/pagination/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'pagination';
2 |
--------------------------------------------------------------------------------
/src/framework/pagination/_pagination.scss:
--------------------------------------------------------------------------------
1 | .pagination {
2 | line-height: 1;
3 | margin: 1em auto;
4 | text-align: center;
5 |
6 | ul {
7 | user-select: none;
8 | list-style-type: none;
9 | display: inline;
10 | font-size: 100%;
11 | margin: 0;
12 | padding: 0.5em;
13 |
14 | a {
15 | color: #2799c4;
16 | font-family: Roboto;
17 | font-size: 13px;
18 | line-height: 15px;
19 |
20 | padding: 0.25em;
21 | }
22 |
23 | li {
24 | display: inline-block;
25 | border-radius: 3px;
26 | }
27 |
28 | .pagination__enabled {
29 | cursor: pointer;
30 | }
31 |
32 | .pagination__page {
33 | min-width: 28px;
34 | }
35 |
36 | a:focus {
37 | color: #526770;
38 | }
39 |
40 | a:hover {
41 | color: #526770;
42 | }
43 | }
44 |
45 | .pagination__disabled a {
46 | color: #526770;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/framework/panel/PanelLayout.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const PanelLayout = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | PanelLayout.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default PanelLayout;
16 |
--------------------------------------------------------------------------------
/src/framework/panel/PanelLayout.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import PanelLayout from './PanelLayout.jsx';
4 |
5 | describe('PanelLayout', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'test',
11 | };
12 | const testCase = TestCaseFactory.create(PanelLayout, props);
13 | expect(testCase.dom.textContent).toBe(props.children);
14 | });
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/panel/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'panel';
3 | @import 'panelLayout';
4 | @import 'progress/panelProgress';
5 |
--------------------------------------------------------------------------------
/src/framework/panel/_panelLayout.scss:
--------------------------------------------------------------------------------
1 | .panelLayout {
2 | display: flex;
3 | align-items: stretch;
4 | height: 100%;
5 | overflow: hidden;
6 |
7 | /**
8 | * TODO: Add Column-like options for specifying different panel widths.
9 | */
10 | .panel {
11 | flex: 1 1 auto;
12 |
13 | &.panel--compactWidth {
14 | flex-grow: 0;
15 | flex-shrink: 0;
16 | }
17 |
18 | /**
19 | * 1. Prevent shrinking.
20 | */
21 | &.panel--fullWidth {
22 | flex-shrink: 0; /* 1 */
23 | }
24 |
25 | }
26 |
27 | .panel + .panel {
28 | border-left: 0;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/framework/panel/progress/PanelProgress.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import Progress from '../../progress/Progress.jsx';
5 |
6 | const PanelProgress = () => (
7 |
10 | );
11 |
12 | export default PanelProgress;
13 |
--------------------------------------------------------------------------------
/src/framework/panel/progress/PanelProgress.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import PanelProgress from './PanelProgress.jsx';
4 |
5 | describe('PanelProgress', () => {
6 | describe('DOM structure', () => {
7 | it('contains a progress element', () => {
8 | const testCase = TestCaseFactory.create(PanelProgress);
9 | expect(testCase.find('.progress').length).toBe(1);
10 | });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/framework/panel/progress/_panelProgress.scss:
--------------------------------------------------------------------------------
1 |
2 | .panelProgress {
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | height: 50px;
7 | background-color: #f6fafc;
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/pickedList/PickedList.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as PickedListItem,
7 | } from './PickedListItem.jsx';
8 |
9 | const PickedList = props => (
10 |
11 |
12 | {props.title}
13 |
14 |
18 | {props.children}
19 |
20 |
21 | );
22 |
23 | PickedList.propTypes = {
24 | dataId: PropTypes.string,
25 | children: PropTypes.any,
26 | title: PropTypes.string.isRequired,
27 | };
28 |
29 | export default PickedList;
30 |
--------------------------------------------------------------------------------
/src/framework/pickedList/PickedList.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import CommonAssertions from '../services/test/CommonAssertions';
4 | import PickedList from './PickedList.jsx';
5 |
6 | describe('PickedList', () => {
7 | describe('Props', () => {
8 | CommonAssertions.assertDataId(PickedList, undefined, '.pickedList');
9 |
10 | describe('children', () => {
11 | it('is rendered', () => {
12 | const props = {
13 | children: 'test',
14 | };
15 | const testCase = TestCaseFactory.create(PickedList, props);
16 | expect(testCase.dom.textContent).toBe(props.children);
17 | });
18 | });
19 |
20 | describe('title', () => {
21 | it('is rendered', () => {
22 | const props = {
23 | title: 'test',
24 | };
25 | const testCase = TestCaseFactory.create(PickedList, props);
26 | expect(testCase.dom.textContent).toBe(props.title);
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/framework/pickedList/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'pickedList';
3 | @import 'pickedListItem';
4 |
--------------------------------------------------------------------------------
/src/framework/pickedList/_pickedList.scss:
--------------------------------------------------------------------------------
1 |
2 | .pickedListContainer {
3 | line-height: $lineHeight;
4 |
5 | & + & {
6 | margin-top: 10px;
7 | }
8 | }
9 |
10 | .pickedListTitle {
11 | font-size: 12px;
12 | margin-bottom: 4px;
13 | }
14 |
15 | .pickedList {
16 | border: 1px solid #d4d8db;
17 | border-radius: 2px;
18 | }
19 |
--------------------------------------------------------------------------------
/src/framework/pickedSummary/PickedSummary.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const PickedSummary = (props) => {
8 | let icon;
9 |
10 | if (props.type) {
11 | const typeToIconClassMap = {
12 | [PickedSummary.TYPE.ALLOWED]: 'icon-check-green',
13 | [PickedSummary.TYPE.NOT_ALLOWED]: 'icon-not-allowed-red',
14 | };
15 |
16 | const iconClasses = classNames(
17 | 'pickedSummaryIcon icon',
18 | typeToIconClassMap[props.type]
19 | );
20 |
21 | icon = ;
22 | }
23 |
24 | return (
25 |
26 | {icon}
27 |
28 | {props.children}
29 |
30 |
31 | );
32 | };
33 |
34 | PickedSummary.TYPE = keyMirror({
35 | ALLOWED: null,
36 | NOT_ALLOWED: null,
37 | });
38 |
39 | PickedSummary.propTypes = {
40 | children: PropTypes.any,
41 | type: PropTypes.string,
42 | };
43 |
44 | export default PickedSummary;
45 |
--------------------------------------------------------------------------------
/src/framework/pickedSummary/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'pickedSummary';
3 |
--------------------------------------------------------------------------------
/src/framework/pickedSummary/_pickedSummary.scss:
--------------------------------------------------------------------------------
1 |
2 | .pickedSummary {
3 | display: flex;
4 | align-items: center;
5 | font-size: 13px;
6 | line-height: $lineHeight;
7 | font-weight: 300;
8 | }
9 |
10 | .pickedSummaryIcon {
11 | $pickedSummaryIconSize: 13px;
12 | width: $pickedSummaryIconSize;
13 | height: $pickedSummaryIconSize;
14 | margin-right: 4px;
15 | left: 1px;
16 | }
17 |
18 | /**
19 | * 1. Display an ellipsis if the text overflows.
20 | */
21 | .pickedSummary__label {
22 | flex: 1;
23 | text-overflow: ellipsis; /* 1 */
24 | white-space: nowrap; /* 1 */
25 | overflow: hidden; /* 1 */
26 | }
27 |
--------------------------------------------------------------------------------
/src/framework/progress/Progress.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const Progress = (props) => {
8 | const sizeToClassMap = {
9 | [Progress.SIZE.SMALL]: 'progress--small',
10 | };
11 |
12 | const classes = classNames(
13 | 'progress',
14 | sizeToClassMap[props.size]
15 | );
16 |
17 | return (
18 |
24 | );
25 | };
26 |
27 | Progress.SIZE = keyMirror({
28 | SMALL: null,
29 | });
30 |
31 | Progress.propTypes = {
32 | size: PropTypes.string,
33 | };
34 |
35 | export default Progress;
36 |
37 | // We need to export these components after the default export because they
38 | // depend on it.
39 | export {
40 | default as ProgressModal,
41 | } from './ProgressModal.jsx';
42 |
43 | export {
44 | default as ProgressSuccess,
45 | } from './ProgressSuccess.jsx';
46 |
--------------------------------------------------------------------------------
/src/framework/progress/Progress.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import Progress from './Progress.jsx';
4 |
5 | describe('Progress', () => {
6 | describe('Props', () => {
7 | describe('size', () => {
8 | describe('SMALL', () => {
9 | it('adds the appropriate class', () => {
10 | const props = {
11 | size: Progress.SIZE.SMALL,
12 | };
13 | const testCase = TestCaseFactory.create(Progress, props);
14 | expect(testCase.dom.className).toContain('progress--small');
15 | });
16 | });
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/framework/progress/ProgressModal.jsx:
--------------------------------------------------------------------------------
1 |
2 | import keyMirror from 'keymirror';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import Progress from './Progress.jsx';
7 | import ProgressSuccess from './ProgressSuccess.jsx';
8 |
9 | const ProgressModal = (props) => {
10 | let content;
11 |
12 | switch (props.type) {
13 | case ProgressModal.TYPE.PROGRESS:
14 | content = ;
15 | break;
16 |
17 | case ProgressModal.TYPE.SUCCESS:
18 | content = ;
19 | break;
20 |
21 | default:
22 | break;
23 | }
24 |
25 | return (
26 |
27 | {content}
28 |
29 | );
30 | };
31 |
32 | ProgressModal.TYPE = keyMirror({
33 | PROGRESS: null,
34 | SUCCESS: null,
35 | });
36 |
37 | ProgressModal.propTypes = {
38 | type: PropTypes.string,
39 | };
40 |
41 | export default ProgressModal;
42 |
--------------------------------------------------------------------------------
/src/framework/progress/ProgressModal.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import ProgressModal from './ProgressModal.jsx';
4 |
5 | describe('ProgressModal', () => {
6 | describe('Props', () => {
7 | describe('type', () => {
8 | it('displays progress when PROGRESS', () => {
9 | const props = {
10 | type: ProgressModal.TYPE.PROGRESS,
11 | };
12 | const testCase = TestCaseFactory.create(ProgressModal, props);
13 | expect(testCase.first('.progress')).toBeDefined();
14 | });
15 |
16 | it('displays success when SUCCESS', () => {
17 | const props = {
18 | type: ProgressModal.TYPE.SUCCESS,
19 | };
20 | const testCase = TestCaseFactory.create(ProgressModal, props);
21 | expect(testCase.first('.progressSuccess')).toBeDefined();
22 | });
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/framework/progress/ProgressSuccess.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const ProgressSuccess = () => (
5 |
6 | );
7 |
8 | export default ProgressSuccess;
9 |
--------------------------------------------------------------------------------
/src/framework/progress/ProgressSuccess.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | describe('ProgressSaved', () => { // eslint-disable-line arrow-body-style
3 | // There's nothing to test in this component.
4 | });
5 |
--------------------------------------------------------------------------------
/src/framework/progress/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'progress';
3 | @import 'progressModal';
4 | @import 'progressSuccess';
5 |
--------------------------------------------------------------------------------
/src/framework/progress/_progressModal.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Optically center content by giving a bit more padding to the bottom.
4 | */
5 | .progressModal {
6 | @include modal;
7 | display: flex;
8 | justify-content: center;
9 | align-items: center;
10 | width: 130px;
11 | height: 90px;
12 | padding-bottom: 6px; /* 1 */
13 | border-radius: 4px;
14 | background: #f7fbfc;
15 | }
16 |
--------------------------------------------------------------------------------
/src/framework/progress/_progressSuccess.scss:
--------------------------------------------------------------------------------
1 |
2 | .progressSuccess {
3 | width: 30px;
4 | height: 30px;
5 | display: inline-block;
6 | }
7 |
--------------------------------------------------------------------------------
/src/framework/radioButtonItem/RadioButtonItem.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const RadioButtonItem = ({
7 | children,
8 | element,
9 | isActive,
10 | onSelect,
11 | }) => {
12 | const classesForRadioButton = classNames({
13 | 'radioButtonItem--inputRadioButton': true,
14 | 'radioButtonItem--inputRadioButton-active': isActive,
15 | });
16 |
17 | return (
18 | onSelect(element)}
21 | >
22 |
25 |
28 | {children}
29 |
30 |
31 | );
32 | };
33 |
34 | RadioButtonItem.defaultProps = {
35 | isActive: false,
36 | };
37 |
38 | RadioButtonItem.propTypes = {
39 | children: PropTypes.oneOfType([
40 | PropTypes.arrayOf(PropTypes.element),
41 | PropTypes.element,
42 | ]),
43 | element: PropTypes.any,
44 | isActive: PropTypes.bool,
45 | onSelect: PropTypes.func.isRequired,
46 | };
47 |
48 | export default RadioButtonItem;
49 |
--------------------------------------------------------------------------------
/src/framework/radioButtonItem/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'radioButtonItem';
3 |
--------------------------------------------------------------------------------
/src/framework/radioButtonItem/_radioButtonItem.scss:
--------------------------------------------------------------------------------
1 | .radioButtonItem {
2 | &--element {
3 | align-items: center;
4 | cursor: pointer;
5 | display: flex;
6 | }
7 |
8 | &--element:not(:last-child) {
9 | margin-bottom: 8px;
10 | }
11 |
12 | &--inputRadioButton {
13 | background: #ffffff;
14 | border-radius: 50%;
15 | border: 1px $borderGrey solid;
16 | display: inline-block;
17 | height: 1em;
18 | width: 1em;
19 |
20 | &-active {
21 | background-color: #47b6f0;
22 | border: 1px solid $borderGrey;
23 | box-shadow: inset 0 0 0 3px #ffffff;
24 | }
25 | }
26 |
27 | &--label {
28 | display: inline-block;
29 | margin-left: 8px;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/radioButtons/RadioButtons.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 |
5 | const RadioButtons = ({
6 | className,
7 | elements,
8 | elementProvider,
9 | onSelect,
10 | }) => {
11 | const radioElements = elements.map((element, index) => (
12 |
13 | {elementProvider(element, onSelect)}
14 |
15 | ));
16 |
17 | return (
18 |
23 | {radioElements}
24 |
25 | );
26 | };
27 |
28 | RadioButtons.propTypes = {
29 | className: PropTypes.string,
30 | elements: PropTypes.arrayOf(PropTypes.shape({
31 | label: PropTypes.string,
32 | value: PropTypes.any,
33 | })).isRequired,
34 | elementProvider: PropTypes.func.isRequired,
35 | onSelect: PropTypes.func.isRequired,
36 | };
37 |
38 | export default RadioButtons;
39 |
--------------------------------------------------------------------------------
/src/framework/ribbon/Ribbon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const Ribbon = props =>
6 | ();
15 |
16 | Ribbon.propTypes = {
17 | height: PropTypes.string,
18 | imageSrc: PropTypes.string,
19 | width: PropTypes.string,
20 | };
21 |
22 | Ribbon.defaultProps = {
23 | height: '75px',
24 | width: '75px',
25 | };
26 |
27 | export default Ribbon;
28 |
--------------------------------------------------------------------------------
/src/framework/ribbon/Ribbon.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import Ribbon from './Ribbon.jsx';
4 |
5 | describe('Ribbon', () => {
6 | describe('DOM structure', () => {
7 | it('has the appropriate class', () => {
8 | const testCase = TestCaseFactory.create(Ribbon, {
9 | imageSrc: 'some image',
10 | });
11 | expect(testCase.dom.className).toContain('ribbon');
12 | });
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/framework/ribbon/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'ribbon';
3 |
--------------------------------------------------------------------------------
/src/framework/ribbon/_ribbon.scss:
--------------------------------------------------------------------------------
1 |
2 | .ribbon {
3 | background-position-x: 0;
4 | background-position-y: 0;
5 | background-repeat: no-repeat;
6 | left: 0;
7 | line-height: 0;
8 | margin: 0;
9 | padding: 0;
10 | pointer-events: none;
11 | position: absolute;
12 | top: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/src/framework/searchBox/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'searchBox';
2 |
--------------------------------------------------------------------------------
/src/framework/searchBox/_searchBox.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1. Ensure we don't inherit anything from containing elements.
4 | */
5 | .searchBox {
6 | position: relative;
7 | line-height: $lineHeight; /* 1 */
8 | font-size: 16px; /* 1 */
9 | }
10 |
11 | $searchBoxHeight: 23px;
12 |
13 | .searchBox__input {
14 | width: 140px;
15 | height: $searchBoxHeight;
16 | margin: 0;
17 | padding: 5px 27px 5px 11px;
18 | border: 1px solid shade($componentStrokeColor, 5%);
19 | border-radius: 20px;
20 | background: white;
21 | font-size: 11px;
22 | color: shade($componentTextColor, 80%);
23 | }
24 |
25 | .searchBox__input--fullWidth {
26 | width: 100%;
27 | }
28 |
29 | /**
30 | * 1. Position icon inside of the searchBox, on the right side, vertically
31 | * centered. Dynamically based on the height of the searchBox.
32 | */
33 | .searchBox__icon {
34 | $searchBoxIconSize: 11px;
35 | $searchBoxIconOffset: ($searchBoxHeight - $searchBoxIconSize) / 2;
36 | position: absolute; /* 1 */
37 | top: $searchBoxIconOffset; /* 1 */
38 | right: $searchBoxIconOffset; /* 1 */
39 | width: $searchBoxIconSize;
40 | height: $searchBoxIconSize;
41 | opacity: 0.16;
42 | }
43 |
--------------------------------------------------------------------------------
/src/framework/services/event/EscapeKeyHandler.js:
--------------------------------------------------------------------------------
1 |
2 | const ESCAPE_KEY_CODE = 27;
3 |
4 | export default class EscapeKeyHandler {
5 |
6 | constructor(callback) {
7 | this.callback = callback;
8 |
9 | this.onKeyDown = this.onKeyDown.bind(this);
10 | this.removeListener = this.removeListener.bind(this);
11 |
12 | document.addEventListener('keydown', this.onKeyDown);
13 | }
14 |
15 | onKeyDown(event) {
16 | if (event.keyCode === ESCAPE_KEY_CODE) {
17 | this.callback();
18 | }
19 | }
20 |
21 | removeListener() {
22 | document.removeEventListener('keydown', this.onKeyDown);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/framework/services/event/polyfillCustomEvent.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Polyfill for IE9 and up.
4 | * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
5 | */
6 |
7 | export default function polyfillCustomEvent() {
8 | function CustomEvent(event, params = {
9 | bubbles: false,
10 | cancelable: false,
11 | detail: undefined,
12 | }) {
13 | const customEvent = document.createEvent('CustomEvent');
14 | customEvent.initCustomEvent(
15 | event,
16 | params.bubbles,
17 | params.cancelable,
18 | params.detail
19 | );
20 | return customEvent;
21 | }
22 |
23 | CustomEvent.prototype = window.Event.prototype;
24 | window.CustomEvent = CustomEvent;
25 | }
26 |
--------------------------------------------------------------------------------
/src/framework/services/filter/ComparisonTypes.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | CONTAINS: 'contains',
4 | DATE_RANGE: 'date range',
5 | MAX: 'max',
6 | MIN: 'min',
7 | MIXED_TYPE_VALUE: 'mixed type value',
8 | ONE_OF: 'one of',
9 | ONE_OF_SEARCH: 'one of search',
10 | };
11 |
--------------------------------------------------------------------------------
/src/framework/services/filter/FilterOption.js:
--------------------------------------------------------------------------------
1 |
2 | export default class FilterOption {
3 |
4 | constructor(config) {
5 | this.comparisonParameters = config.comparisonParameters;
6 | this.comparisonType = config.comparisonType;
7 | this.getValue = config.getValue;
8 | this.name = config.name;
9 | this.isRemovable = config.isRemovable !== false;
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/framework/services/filter/FilterableItems.js:
--------------------------------------------------------------------------------
1 |
2 | export default class FilterableItems {
3 |
4 | constructor(items) {
5 | this.items = items;
6 | }
7 |
8 | applyFilters(filters) {
9 | return this.items.filter(item => (
10 | filters.every(filter => filter.doesItemPass(item))
11 | ));
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/framework/services/filter/MixedTypeValueFilter.js:
--------------------------------------------------------------------------------
1 |
2 | export default class MixedTypeValueFilter {
3 |
4 | constructor(discreteValues, inputValue) {
5 | this.discreteValues = discreteValues;
6 | this.inputValue = inputValue;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/services/filter/MixedTypeValueFilter.spec.js:
--------------------------------------------------------------------------------
1 |
2 | import MixedTypeValueFilter from './MixedTypeValueFilter';
3 |
4 | describe('MixedTypeValueFilter', () => {
5 | describe('interface', () => {
6 | describe('constructor method', () => {
7 | it(
8 | 'accepts discreteValues and an inputValue and surfaces them',
9 | () => {
10 | const discreteValues = [{ label: 'Test', value: 'test' }];
11 | const inputValue = '42';
12 | const filter = new MixedTypeValueFilter(discreteValues, inputValue);
13 | expect(filter.discreteValues).toBe(discreteValues);
14 | expect(filter.inputValue).toBe(inputValue);
15 | }
16 | );
17 | });
18 | });
19 | });
20 |
21 |
--------------------------------------------------------------------------------
/src/framework/services/filter/OneOfOption.js:
--------------------------------------------------------------------------------
1 |
2 | export default class OneOfOption {
3 |
4 | constructor(value, label) {
5 | this.value = value;
6 | this.label = label || value;
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/framework/services/filter/OneOfOption.spec.js:
--------------------------------------------------------------------------------
1 |
2 | import OneOfOption from './OneOfOption';
3 |
4 | describe('OneOfOption', () => {
5 | describe('interface', () => {
6 | describe('constructor method', () => {
7 | it('label is same as value when is not given', () => {
8 | const oneOfOption = new OneOfOption('active');
9 | expect(oneOfOption.label).toBe(oneOfOption.value);
10 | });
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/framework/services/number/Number.js:
--------------------------------------------------------------------------------
1 |
2 | const POWER_TO_LABEL_MAP = {
3 | 1: 'k',
4 | 2: 'm',
5 | 3: 'b',
6 | };
7 |
8 | function formatBigNumber(number) {
9 | const absolute = Math.abs(number);
10 | // Floor of log (base 1000) of number, e.g. 1,234,567 -> 2
11 | const power = Math.floor(Math.log(absolute) / Math.log(1000));
12 | const label = POWER_TO_LABEL_MAP[power];
13 |
14 | if (label) {
15 | return `${Math.round(number / (1000 ** power))}${label}`;
16 | }
17 | return `${Math.round(number)}`;
18 | }
19 |
20 | export default {
21 | formatBigNumber,
22 | };
23 |
--------------------------------------------------------------------------------
/src/framework/services/sort/SortState.js:
--------------------------------------------------------------------------------
1 |
2 | export default class SortState {
3 |
4 | constructor(config) {
5 | this.descendingProperty = config.descendingProperty;
6 | this.indexProperty = config.indexProperty;
7 |
8 | this.state = {
9 | [this.descendingProperty]: config.isDescending,
10 | [this.indexProperty]: config.index,
11 | };
12 | }
13 |
14 | sortOn(index) {
15 | const currentIndex = this.state[this.indexProperty];
16 | const isCurrentlyDescending = this.state[this.descendingProperty];
17 |
18 | const isDescending =
19 | currentIndex === index
20 | ? !isCurrentlyDescending
21 | : isCurrentlyDescending;
22 |
23 | this.state[this.indexProperty] = index;
24 | this.state[this.descendingProperty] = isDescending;
25 | }
26 |
27 | getState() {
28 | return this.state;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/framework/services/string/Entity.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * A convenience wrapper for rendering HTML entities in React components.
4 | * https://facebook.github.io/react/docs/jsx-gotchas.html
5 | */
6 |
7 | const NON_BREAKING_SPACE = String.fromCharCode(160);
8 |
9 | export default {
10 | nbsp: NON_BREAKING_SPACE,
11 | };
12 |
--------------------------------------------------------------------------------
/src/framework/services/test/CommonAssertions.js:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 |
4 | function assertDataId(Component, props, selector) {
5 | describe('dataId', () => {
6 | const elementName = selector ? `${selector}` : 'root element';
7 | it(`is added to ${elementName}`, () => {
8 | const extendedProps = Object.assign({}, props, {
9 | dataId: 'dataId',
10 | });
11 | const testCase = TestCaseFactory.create(Component, extendedProps);
12 | const element = selector ? testCase.first(selector) : testCase.dom;
13 | expect(element.getAttribute('data-id')).toBe(extendedProps.dataId);
14 | });
15 | });
16 | }
17 |
18 | const CommonAssertions = {
19 | assertDataId,
20 | };
21 |
22 | export default CommonAssertions;
23 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/StatusDropdownOption.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import BaseDropdownOption from '../base/dropdown/BaseDropdownOption.jsx';
5 |
6 | const StatusDropdownOption = props => (
7 |
10 | );
11 |
12 | StatusDropdownOption.propTypes = BaseDropdownOption.propTypes;
13 |
14 | StatusDropdownOption.defaultProps = {
15 | classes: 'statusDropdownOption',
16 | focusClasses: 'is-status-dropdown-option-focus',
17 | selectedClasses: 'is-status-dropdown-option-selected',
18 | };
19 |
20 | export default StatusDropdownOption;
21 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/StatusDropdownOption.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import StatusDropdownOption from './StatusDropdownOption.jsx';
4 | import BaseDropdownOption from '../base/dropdown/BaseDropdownOption.jsx';
5 |
6 | describe('StatusDropdownOption', () => {
7 | describe('DOM structure', () => {
8 | it('is a BaseDropdownOption', () => {
9 | const props = {
10 | onClick: () => undefined,
11 | onMouseOver: () => undefined,
12 | };
13 | const testCase = TestCaseFactory.create(StatusDropdownOption, props);
14 | expect(testCase.findComponents(BaseDropdownOption)).toBeDefined();
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'statusDropdown';
3 | @import 'statusDropdownOption';
4 | @import 'statusDropdownOptionIcon/index';
5 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/_statusDropdownOption.scss:
--------------------------------------------------------------------------------
1 |
2 | .statusDropdownOption {
3 | border-bottom: 1px solid #e1e1e1;
4 | cursor: pointer;
5 | font-size: 12px;
6 | line-height: 20px;
7 | padding: 4px 8px;
8 |
9 | &:last-child {
10 | border-bottom: 0;
11 | }
12 |
13 | &.is-status-dropdown-option-selected {
14 | filter: grayscale(100%);
15 |
16 | .statusDropdownOptionIcon.icon-check {
17 | margin-left: 10px;
18 | opacity: 0.5;
19 | }
20 | }
21 |
22 | .statusDropdownOptionIcon {
23 | margin-right: 5px;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/statusDropdownOptionIcon/StatusDropdownOptionIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | const StatusDropdownOptionIcon = (props) => {
8 | const classMap = {
9 | [StatusDropdownOptionIcon.TYPE.ACTIVATE]: 'icon-play-blue',
10 | [StatusDropdownOptionIcon.TYPE.ARCHIVE]: 'icon-remove-circle-blue',
11 | [StatusDropdownOptionIcon.TYPE.DEACTIVATE]: 'icon-pause-blue',
12 | [StatusDropdownOptionIcon.TYPE.SELECTED]: 'icon-check',
13 | };
14 |
15 | const classes = classNames(
16 | 'statusDropdownOptionIcon icon',
17 | classMap[props.type]
18 | );
19 |
20 | return (
21 |
22 | );
23 | };
24 |
25 | StatusDropdownOptionIcon.TYPE = keyMirror({
26 | ACTIVATE: null,
27 | ARCHIVE: null,
28 | DEACTIVATE: null,
29 | SELECTED: null,
30 | });
31 |
32 | StatusDropdownOptionIcon.propTypes = {
33 | type: PropTypes.string.isRequired,
34 | };
35 |
36 | export default StatusDropdownOptionIcon;
37 |
--------------------------------------------------------------------------------
/src/framework/statusDropdown/statusDropdownOptionIcon/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | $statusDropdownOptionIconSize: 10px;
3 |
4 | .statusDropdownOptionIcon {
5 | display: inline-block;
6 | height: $statusDropdownOptionIconSize;
7 | width: $statusDropdownOptionIconSize;
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/summaryControl/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'summaryControl';
3 |
--------------------------------------------------------------------------------
/src/framework/summaryControl/_summaryControl.scss:
--------------------------------------------------------------------------------
1 |
2 | $summaryControlIconSize: 12px;
3 |
4 | .summaryControl {
5 | display: flex;
6 | justify-content: space-between;
7 | align-items: center;
8 | height: 34px;
9 | padding: 0 8px;
10 | line-height: 1.2;
11 | color: #444444;
12 | border: 1px solid #c3cfd6;
13 | font-size: 12px;
14 | border-radius: 3px;
15 | box-shadow: 0 1px rgba(#c3cfd6, 0.25);
16 | cursor: pointer;
17 | }
18 |
19 | .summaryControl__icon {
20 | height: $summaryControlIconSize;
21 | width: $summaryControlIconSize;
22 | vertical-align: top;
23 | margin-right: 5px;
24 | }
25 |
26 | .summaryControl__label {
27 | flex: 1;
28 | overflow: hidden;
29 | text-overflow: ellipsis;
30 | white-space: nowrap;
31 | }
32 |
33 | .summaryControl__cogIcon {
34 | height: $summaryControlIconSize;
35 | width: $summaryControlIconSize;
36 | opacity: 0.25;
37 | }
38 |
39 | /**
40 | * 1. Don't fill container width.
41 | * 2. Non-clickable.
42 | */
43 | .summaryControl--static {
44 | display: inline-flex; /* 1 */
45 | box-shadow: none; /* 2 */
46 | cursor: default; /* 2 */
47 | }
48 |
--------------------------------------------------------------------------------
/src/framework/text/DescriptionText.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const DescriptionText = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | DescriptionText.propTypes = {
12 | children: PropTypes.any,
13 | };
14 |
15 | export default DescriptionText;
16 |
--------------------------------------------------------------------------------
/src/framework/text/DescriptionText.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import DescriptionText from './DescriptionText.jsx';
4 |
5 | describe('DescriptionText', () => {
6 | describe('Props', () => {
7 | describe('children', () => {
8 | it('is rendered as the textContent of the component', () => {
9 | const props = {
10 | children: 'Test',
11 | };
12 |
13 | const testCase = TestCaseFactory.create(
14 | DescriptionText,
15 | props
16 | );
17 | expect(testCase.dom.textContent).toBe(props.children);
18 | });
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/framework/text/Heading.jsx:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import keyMirror from 'keymirror';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const Heading = (props) => {
7 | const sizeClassMap = {
8 | [Heading.SIZE.LARGE]: 'heading--large',
9 | [Heading.SIZE.SMALL]: 'heading--small',
10 | };
11 |
12 | const classes = classNames('heading', sizeClassMap[props.size]);
13 |
14 | return (
15 |
16 | {props.children}
17 |
18 | );
19 | };
20 |
21 | Heading.SIZE = keyMirror({
22 | LARGE: null,
23 | SMALL: null,
24 | });
25 |
26 | Heading.propTypes = {
27 | children: PropTypes.any,
28 | size: PropTypes.string,
29 | };
30 |
31 | Heading.defaultProps = {
32 | size: Heading.SIZE.LARGE,
33 | };
34 |
35 | export default Heading;
36 |
--------------------------------------------------------------------------------
/src/framework/text/Text.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import keyMirror from 'keymirror';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 |
7 | export {
8 | default as DescriptionText,
9 | } from './DescriptionText.jsx';
10 |
11 | export {
12 | default as Heading,
13 | } from './Heading.jsx';
14 |
15 | const Text = (props) => {
16 | const rhythmClassMap = {
17 | [Text.RHYTHM.XSMALL]: 'text--xSmallRhythm',
18 | [Text.RHYTHM.SMALL]: 'text--smallRhythm',
19 | };
20 |
21 | const classes = classNames('text', rhythmClassMap[props.rhythm]);
22 |
23 | return (
24 |
25 | {props.children}
26 |
27 | );
28 | };
29 |
30 | Text.RHYTHM = keyMirror({
31 | XSMALL: null,
32 | SMALL: null,
33 | });
34 |
35 | Text.propTypes = {
36 | children: PropTypes.any,
37 | rhythm: PropTypes.string,
38 | };
39 |
40 | export default Text;
41 |
--------------------------------------------------------------------------------
/src/framework/text/_descriptionText.scss:
--------------------------------------------------------------------------------
1 | .descriptionText {
2 | font-size: 12px;
3 | font-weight: 400;
4 | color: #8e9aa2;
5 | line-height: $lineHeight;
6 | }
7 |
--------------------------------------------------------------------------------
/src/framework/text/_heading.scss:
--------------------------------------------------------------------------------
1 | .heading--large {
2 | color: #2e9ac2;
3 | font-size: 30px;
4 | font-weight: 400;
5 | letter-spacing: 0.5px;
6 | }
7 |
8 | .heading--small {
9 | font-size: 14px;
10 | font-weight: 500;
11 | }
12 |
--------------------------------------------------------------------------------
/src/framework/text/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'text';
2 | @import 'descriptionText';
3 | @import 'heading';
4 |
--------------------------------------------------------------------------------
/src/framework/text/_text.scss:
--------------------------------------------------------------------------------
1 | .text {
2 | font-size: 13px;
3 | font-weight: 400;
4 | color: $textColor;
5 | line-height: $lineHeight;
6 | }
7 |
8 | .text--xSmallRhythm {
9 | margin-bottom: 2px;
10 | }
11 |
12 | .text--smallRhythm {
13 | margin-bottom: 10px;
14 | }
15 |
--------------------------------------------------------------------------------
/src/framework/textArea/TextArea.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const TextArea = (props) => {
7 | const { dataId, isError, isFullWidth, isResizable, ...reducedProps } = props;
8 | const classes = classNames('textArea', props.className, {
9 | 'is-text-box-error': isError,
10 | 'textArea--fullWidth': isFullWidth,
11 | 'textArea--resizable': isResizable,
12 | });
13 |
14 | const extendedProps = Object.assign({}, reducedProps, {
15 | className: classes,
16 | });
17 |
18 | return (
19 |
23 | );
24 | };
25 |
26 | TextArea.propTypes = {
27 | className: PropTypes.oneOfType([
28 | PropTypes.object,
29 | PropTypes.array,
30 | PropTypes.string,
31 | ]),
32 | dataId: PropTypes.string,
33 | isError: PropTypes.bool,
34 | isFullWidth: PropTypes.bool,
35 | isResizable: PropTypes.bool,
36 | };
37 |
38 | export default TextArea;
39 |
--------------------------------------------------------------------------------
/src/framework/textArea/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'textArea';
3 |
--------------------------------------------------------------------------------
/src/framework/textArea/_textArea.scss:
--------------------------------------------------------------------------------
1 |
2 | .textArea {
3 | @include field;
4 | padding: $fieldPadding;
5 | min-height: 65px;
6 | resize: none;
7 | }
8 |
9 | .textArea--fullWidth {
10 | width: 100%;
11 | }
12 |
13 | .textArea--resizable {
14 | resize: vertical;
15 | }
16 |
17 | .is-text-box-error {
18 | @include fieldError;
19 | }
20 |
--------------------------------------------------------------------------------
/src/framework/textInput/TextInput.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const TextInput = (props) => {
7 | const {
8 | dataId,
9 | isError,
10 | isFullWidth,
11 | isReadonly,
12 | ...reducedProps
13 | } = props;
14 |
15 | const classes = classNames('textInput', props.className, {
16 | 'textInput--fullWidth': isFullWidth,
17 | 'is-text-input-error': isError,
18 | });
19 |
20 | const extendedProps = Object.assign({}, reducedProps, {
21 | className: classes,
22 | });
23 |
24 | return (
25 |
30 | );
31 | };
32 |
33 | TextInput.propTypes = {
34 | className: PropTypes.oneOfType([
35 | PropTypes.object,
36 | PropTypes.array,
37 | PropTypes.string,
38 | ]),
39 | dataId: PropTypes.string,
40 | isError: PropTypes.bool,
41 | isFullWidth: PropTypes.bool,
42 | isReadonly: PropTypes.bool,
43 | };
44 |
45 | export default TextInput;
46 |
--------------------------------------------------------------------------------
/src/framework/textInput/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'textInput';
3 |
--------------------------------------------------------------------------------
/src/framework/textInput/_textInput.scss:
--------------------------------------------------------------------------------
1 |
2 | .textInput {
3 | @include field;
4 | height: $textInput_height;
5 | padding: 0 $fieldPadding;
6 | }
7 |
8 | .textInput--fullWidth {
9 | width: 100%;
10 | }
11 |
12 | .is-text-input-error {
13 | @include fieldError;
14 | }
15 |
--------------------------------------------------------------------------------
/src/framework/titleBar/TitleBar.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const TitleBar = (props) => {
6 | let buttons;
7 |
8 | if (props.buttons) {
9 | buttons = props.buttons.map((button, index) => (
10 |
11 | {button}
12 |
13 | ));
14 | }
15 |
16 | return (
17 |
18 |
19 | {props.label}
20 |
21 |
22 | {buttons}
23 |
24 |
25 | );
26 | };
27 |
28 | TitleBar.propTypes = {
29 | label: PropTypes.string,
30 | buttons: PropTypes.array,
31 | };
32 |
33 | export default TitleBar;
34 |
--------------------------------------------------------------------------------
/src/framework/titleBar/TitleBar.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { TestCaseFactory } from 'react-test-kit';
4 | import TitleBar from './TitleBar.jsx';
5 |
6 | describe('TitleBar', () => {
7 | describe('Props', () => {
8 | describe('label', () => {
9 | it('is rendered', () => {
10 | const props = {
11 | label: 'title',
12 | };
13 | const testCase = TestCaseFactory.create(TitleBar, props);
14 | expect(testCase.dom.textContent).toBe(props.label);
15 | });
16 | });
17 |
18 | describe('buttons', () => {
19 | it('are rendered', () => {
20 | const props = {
21 | buttons: [
22 | button
,
23 | ],
24 | };
25 | const testCase = TestCaseFactory.create(TitleBar, props);
26 | expect(testCase.dom.textContent).toBe('button');
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/framework/titleBar/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'titleBar';
3 |
--------------------------------------------------------------------------------
/src/framework/toggle/Toggle.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import ReactToggle from 'react-toggle';
4 | import PropTypes from 'prop-types';
5 |
6 | const Toggle = (props) => {
7 | let icons = false;
8 |
9 | if (props.isLabel) {
10 | icons = {
11 | checked: 'ON',
12 | unchecked: 'OFF',
13 | };
14 | }
15 |
16 | const onChange = (event) => {
17 | props.onChange(event.target.checked);
18 | };
19 |
20 | return (
21 |
26 | );
27 | };
28 |
29 | Toggle.propTypes = {
30 | checked: PropTypes.bool,
31 | isLabel: PropTypes.bool,
32 | onChange: PropTypes.func.isRequired,
33 | };
34 |
35 | export default Toggle;
36 |
--------------------------------------------------------------------------------
/src/framework/toggle/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'toggle';
3 |
--------------------------------------------------------------------------------
/src/framework/tooltip/Tooltip.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { TestCaseFactory } from 'react-test-kit';
3 | import Tooltip from './Tooltip.jsx';
4 |
5 | describe('Tooltip', () => {
6 | describe('Props', () => {
7 | describe('childen', () => {
8 | it('is rendered', () => {
9 | const props = {
10 | children: 'hi',
11 | };
12 |
13 | const testCase = TestCaseFactory.create(Tooltip, props);
14 | expect(testCase.dom.textContent).toContain(props.children);
15 | });
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/framework/tooltip/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'tooltip';
3 |
--------------------------------------------------------------------------------
/src/framework/tooltip/_tooltip.scss:
--------------------------------------------------------------------------------
1 |
2 | $arrowSide: 6px;
3 |
4 | .tooltip {
5 | display: inline-block;
6 | line-height: 0;
7 | position: relative;
8 |
9 | &__container {
10 | display: inline-block;
11 | position: absolute;
12 | z-index: 10;
13 |
14 | &:after {
15 | background-color: $white;
16 | border: solid $componentStrokeColor;
17 | border-width: 1px 0 0 1px;
18 | content: '';
19 | display: block;
20 | height: $arrowSide;
21 | position: absolute;
22 | margin-left: -$arrowSide;
23 | margin-top: -3px;
24 | right: 20px;
25 | transform: rotate(45deg);
26 | top: 0;
27 | width: $arrowSide;
28 | }
29 |
30 | &.tooltipTop:after {
31 | border-width: 0 1px 1px 0;
32 | top: initial;
33 | }
34 |
35 | &.tooltipRight:after {
36 | left: 20px;
37 | }
38 |
39 | .box {
40 | box-shadow: 0 2px 4px 0 rgba(189, 189, 189, 0.5);
41 | }
42 |
43 | &__content {
44 | height: 100%;
45 | padding: 15px 13px;
46 | width: 100%;
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/framework/verticalLayout/VerticalLayout.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const VerticalLayout = (props) => {
6 | function wrap(item, index) {
7 | return (
8 |
12 | {item}
13 |
14 | );
15 | }
16 |
17 | let children;
18 |
19 | if (Array.isArray(props.children)) {
20 | children = props.children.map((item, index) => wrap(item, index));
21 | } else if (props.children) {
22 | children = wrap(props.children);
23 | }
24 |
25 | return (
26 |
27 | {children}
28 |
29 | );
30 | };
31 |
32 | VerticalLayout.propTypes = {
33 | children: PropTypes.any,
34 | };
35 |
36 | export default VerticalLayout;
37 |
--------------------------------------------------------------------------------
/src/framework/verticalLayout/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'verticalLayout';
2 |
--------------------------------------------------------------------------------
/src/framework/verticalLayout/_verticalLayout.scss:
--------------------------------------------------------------------------------
1 | .verticalLayoutItem {
2 | margin-bottom: 20px;
3 | line-height: $lineHeight;
4 |
5 | &:last-child {
6 | margin-bottom: 0;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/framework/viewHeader/ViewHeader.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | export {
6 | default as ViewHeaderNav,
7 | } from './nav/ViewHeaderNav.jsx';
8 |
9 | export {
10 | default as DateRange,
11 | } from './dateRange/DateRange.jsx';
12 |
13 | const ViewHeader = props => (
14 |
15 |
16 | {props.left}
17 | {props.right}
18 |
19 |
20 | );
21 |
22 | ViewHeader.propTypes = {
23 | left: PropTypes.element,
24 | right: PropTypes.element,
25 | };
26 |
27 | export default ViewHeader;
28 |
--------------------------------------------------------------------------------
/src/framework/viewHeader/_index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import 'viewHeader';
3 | @import 'nav/viewHeaderNav';
4 | @import 'dateRange/dateRange';
5 |
--------------------------------------------------------------------------------
/src/framework/viewHeader/_viewHeader.scss:
--------------------------------------------------------------------------------
1 | .viewHeader {
2 | background: $viewHeaderNav_backgroundColor;
3 | display: flex;
4 | justify-content: center;
5 | height: 42px;
6 | width: 100%;
7 | }
8 |
9 | /**
10 | * 1. This allows children to take height from component root element.
11 | * 2. Account for borders of BodyPanel, so that this component visually aligns
12 | * with the width of the BodyPanel component.
13 | */
14 | .viewHeader__liner {
15 | display: flex;
16 | align-items: center;
17 | justify-content: space-between;
18 | height: inherit; /* 1 */
19 | flex: 1;
20 | max-width: $maxWidth - 2px; /* 2 */
21 | padding: 0 15px;
22 | }
23 |
--------------------------------------------------------------------------------
/src/framework/viewHeader/dateRange/_dateRange.scss:
--------------------------------------------------------------------------------
1 |
2 | .dateRange {
3 | display: inline-block;
4 | font-size: 12px;
5 | line-height: 12px;
6 | position: relative;
7 |
8 | .rdrMonth {
9 | width: 250px;
10 | }
11 | }
12 |
13 | .dateRange__range {
14 | border: 1px solid transparent;
15 | border-radius: 2px;
16 | color: $viewHeaderNav_textColor;
17 | cursor: pointer;
18 | font-weight: 400;
19 | padding: 5px 10px;
20 |
21 | &:hover,
22 | &.dateRange__range--active {
23 | background-color: rgba(black, 0.05);
24 | border: 1px solid rgba(white, 0.2);
25 | }
26 |
27 | .icon-calendar-white {
28 | display: inline-block;
29 | margin-right: 4px;
30 | height: 12px;
31 | vertical-align: text-top;
32 | width: 12px;
33 | }
34 | }
35 |
36 | .dateRange__arrow {
37 | border-bottom: 5px solid #ffffff;
38 | border-left: 5px solid transparent;
39 | border-right: 5px solid transparent;
40 | height: 0;
41 | margin: 3px auto -8px;
42 | width: 0;
43 | }
44 |
45 | .dateRange__picker {
46 | margin-top: 8px;
47 | position: absolute;
48 | z-index: 1;
49 | }
50 |
--------------------------------------------------------------------------------
/src/framework/viewHeader/nav/ViewHeaderNav.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | const ViewHeaderNav = (props) => {
7 | const links = props.links.map((link, index) => (
8 | React.createElement(props.linkType, Object.assign({}, link, {
9 | className: classNames('viewHeaderNav__link', link.className),
10 | key: index,
11 | }))
12 | ));
13 |
14 | return (
15 |
18 | );
19 | };
20 |
21 | ViewHeaderNav.propTypes = {
22 | linkType: PropTypes.oneOfType([
23 | PropTypes.string,
24 | PropTypes.func,
25 | ]).isRequired,
26 | links: PropTypes.arrayOf(PropTypes.object).isRequired,
27 | };
28 |
29 | ViewHeaderNav.ACTIVE_LINK_CLASS_NAME = 'is-view-header-nav-link-selected';
30 |
31 | export default ViewHeaderNav;
32 |
--------------------------------------------------------------------------------
/src/guide/components/navigation/NavItem.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import { Link } from 'react-router';
6 |
7 | const NavItem = (props) => {
8 | if (props.href) {
9 | return (
10 |
17 | {props.children}
18 |
19 | );
20 | }
21 |
22 | return (
23 |
28 | {props.children}
29 |
30 | );
31 | };
32 |
33 | NavItem.propTypes = {
34 | children: PropTypes.string,
35 | onClick: PropTypes.func.isRequired,
36 | path: PropTypes.string,
37 | href: PropTypes.string,
38 | };
39 |
40 | export default NavItem;
41 |
--------------------------------------------------------------------------------
/src/guide/components/navigation/NavTitle.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const NavTitle = props => (
6 |
9 | {props.children}
10 |
11 | );
12 |
13 | NavTitle.propTypes = {
14 | children: PropTypes.string.isRequired,
15 | };
16 |
17 | export default NavTitle;
18 |
--------------------------------------------------------------------------------
/src/guide/components/page/Example.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import SubTitle from './SubTitle.jsx';
7 |
8 | const Example = (props) => {
9 | const classes = classNames('exampleContainer', {
10 | 'exampleContainer--clear': props.isClear,
11 | 'exampleContainer--dark': props.isDark,
12 | });
13 |
14 | let title;
15 | if (props.title) {
16 | title = (
17 | {props.title}
18 | );
19 | }
20 |
21 | return (
22 |
23 | {title}
24 | {props.children}
25 |
26 | );
27 | };
28 |
29 | Example.propTypes = {
30 | title: PropTypes.string,
31 | isClear: PropTypes.bool,
32 | isDark: PropTypes.bool,
33 | children: PropTypes.oneOfType([
34 | PropTypes.element,
35 | PropTypes.array,
36 | ]),
37 | };
38 |
39 | export default Example;
40 |
--------------------------------------------------------------------------------
/src/guide/components/page/Page.jsx:
--------------------------------------------------------------------------------
1 |
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import React from 'react';
5 |
6 | import Title from './Title.jsx';
7 |
8 | export { default as Example } from './Example.jsx';
9 | export { default as Title } from './Title.jsx';
10 | export { default as SubTitle } from './SubTitle.jsx';
11 | export { default as Text } from './Text.jsx';
12 |
13 | const Page = (props) => {
14 | const classes = classNames('examplePage', {
15 | 'examplePage--fullScreen': props.isFullScreen,
16 | });
17 |
18 | let title;
19 | if (props.title) {
20 | title = (
21 | {props.title}
22 | );
23 | }
24 |
25 | return (
26 |
27 | {title}
28 | {props.children}
29 |
30 | );
31 | };
32 |
33 | Page.propTypes = {
34 | children: PropTypes.oneOfType([
35 | PropTypes.arrayOf(PropTypes.element),
36 | PropTypes.element,
37 | ]),
38 | isFullScreen: PropTypes.bool,
39 | title: PropTypes.string,
40 | };
41 |
42 | export default Page;
43 |
--------------------------------------------------------------------------------
/src/guide/components/page/SubTitle.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const SubTitle = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | SubTitle.propTypes = {
12 | children: PropTypes.string.isRequired,
13 | };
14 |
15 | export default SubTitle;
16 |
--------------------------------------------------------------------------------
/src/guide/components/page/Text.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const Text = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | Text.propTypes = {
12 | children: PropTypes.oneOfType([
13 | PropTypes.array,
14 | PropTypes.element,
15 | PropTypes.string,
16 | ]).isRequired,
17 | };
18 |
19 | export default Text;
20 |
--------------------------------------------------------------------------------
/src/guide/components/page/Title.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const Title = props => (
6 |
7 | {props.children}
8 |
9 | );
10 |
11 | Title.propTypes = {
12 | children: PropTypes.string.isRequired,
13 | };
14 |
15 | export default Title;
16 |
--------------------------------------------------------------------------------
/src/guide/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang='en')
3 |
4 | head
5 |
6 | //- We need to specify the charset in order to use D3.
7 | meta(
8 | http-equiv="content-type"
9 | content="text/html; charset=UTF8"
10 | )
11 |
12 | link(
13 | rel='stylesheet'
14 | href='./css/index.css?v=#{DATE_TIME}'
15 | )
16 |
17 | //- Fonts
18 | link(
19 | href = "//fonts.googleapis.com/css?family=Ubuntu+Mono|400"
20 | rel = "stylesheet"
21 | type = "text/css"
22 | )
23 |
24 | body
25 | div
26 | #app
27 | div
28 | #portal-div
29 | script(
30 | src='./js/dist.js?v=#{DATE_TIME}'
31 | )
32 |
--------------------------------------------------------------------------------
/src/guide/services/string/Slug.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Lowercases input and replaces spaces with hyphens:
4 | * e.g. 'GridView Example' -> 'gridview-example'
5 | */
6 | export function slugify(str) {
7 | const parts = str
8 | .toLowerCase()
9 | .replace(/[-]+/g, ' ')
10 | .replace(/[^\w^\s]+/g, '')
11 | .replace(/ +/g, ' ')
12 | .split(' ');
13 | return parts.join('-');
14 | }
15 |
16 | export function slugifyEach(items, src, dest) {
17 | return items.map((item) => {
18 | const itemRef = item;
19 | itemRef[dest] = slugify(itemRef[src]);
20 | return itemRef;
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/src/guide/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import {
2 | applyMiddleware,
3 | createStore,
4 | compose,
5 | } from 'redux';
6 | import thunk from 'redux-thunk';
7 | import { browserHistory } from 'react-router';
8 | import {
9 | routerMiddleware,
10 | routerReducer,
11 | } from 'react-router-redux';
12 |
13 | /**
14 | * @param {Object} initialState An object defining the application's initial
15 | * state.
16 | */
17 | export default function configureStore(initialState) {
18 | function rootReducer(state = {}, action) {
19 | return {
20 | routing: routerReducer(state.routing, action),
21 | };
22 | }
23 |
24 | const finalStore = compose(
25 | applyMiddleware(
26 | thunk,
27 | routerMiddleware(browserHistory)
28 | )
29 | )(createStore)(rootReducer, initialState);
30 |
31 | return finalStore;
32 | }
33 |
--------------------------------------------------------------------------------
/src/guide/views/AppContainer.js:
--------------------------------------------------------------------------------
1 |
2 | import { connect } from 'react-redux';
3 | import AppView from './AppView.jsx';
4 |
5 | function mapStateToProps(state, ownProps) {
6 | return {
7 | routes: ownProps.routes,
8 | };
9 | }
10 |
11 | export default connect(mapStateToProps)(AppView);
12 |
--------------------------------------------------------------------------------
/src/guide/views/_app.scss:
--------------------------------------------------------------------------------
1 | .app {
2 | position: relative;
3 | transition: padding-right 0.25s ease-out;
4 |
5 | &.is-source-code-viewer-open {
6 | padding-right: $sourceCodeViewerWidth;
7 | }
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/guide/views/box/BoxExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | Box,
11 | } from '../../../framework/framework';
12 |
13 | const BoxExample = props => (
14 |
15 |
16 |
17 | This is a simple box.
18 |
19 |
20 |
21 |
22 | This is a simple box with rounded corners.
23 |
24 |
25 |
26 | );
27 |
28 | BoxExample.propTypes = {
29 | route: PropTypes.object.isRequired,
30 | };
31 |
32 | export default BoxExample;
33 |
--------------------------------------------------------------------------------
/src/guide/views/card/_cardExample.scss:
--------------------------------------------------------------------------------
1 |
2 | .card__footer--right__status {
3 | .statusDropdown {
4 | text-align: center;
5 | }
6 |
7 | .statusDropdownOptionList {
8 | bottom: -8px;
9 | top: inherit;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/guide/views/card/blueRibbon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smaato/ui-framework/HEAD/src/guide/views/card/blueRibbon.png
--------------------------------------------------------------------------------
/src/guide/views/home/HomeView.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import Page from '../../components/page/Page.jsx';
5 |
6 | const HomeView = () => (
7 |
8 |
9 |
10 |
11 | Smaato UI Framework
12 |
13 |
14 | Click the "Examples" button in the top left corner to
15 | view the examples.
16 |
17 |
18 | Click the "Source" button in the top right corner to
19 | view the source code for the example you're viewing.
20 |
21 |
22 |
26 | See the full source on GitHub.
27 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | export default HomeView;
35 |
--------------------------------------------------------------------------------
/src/guide/views/home/_homeView.scss:
--------------------------------------------------------------------------------
1 |
2 | .examplesHome {
3 | position: fixed;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | justify-content: center;
12 | padding: 5% 40px 12%;
13 | font-size: 24px;
14 | line-height: 36px;
15 | font-weight: 700;
16 | }
17 |
18 | .examplesHomeContent {
19 | padding: 10px 410px 0 30px;
20 | max-width: 900px;
21 | border-left: 10px solid #bfbfc5;
22 |
23 | @include whenNarrowerThan(1200px) {
24 | padding-right: 30px;
25 | }
26 | }
27 |
28 | .examplesHome__title {
29 | font-size: 44px;
30 | font-weight: 300;
31 | margin-bottom: 24px;
32 | line-height: 1;
33 | }
34 |
35 | .examplesHome__text {
36 | font-size: 14px;
37 | font-weight: 400;
38 | line-height: 1.5;
39 | margin-bottom: 18px;
40 | }
41 |
42 | .examplesHome__link {
43 | &:hover {
44 | text-decoration: underline;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/guide/views/horizontalLine/HorizontalLineExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | HorizontalLine,
11 | } from '../../../framework/framework';
12 |
13 | const HorizontalLineExample = props => (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 |
23 | HorizontalLineExample.propTypes = {
24 | route: PropTypes.object.isRequired,
25 | };
26 |
27 | export default HorizontalLineExample;
28 |
--------------------------------------------------------------------------------
/src/guide/views/kpi/KpiExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | Text,
8 | } from '../../components/page/Page.jsx';
9 |
10 | import {
11 | Kpi,
12 | KpiPositive,
13 | KpiNegative,
14 | } from '../../../framework/framework';
15 |
16 | const KpiExample = props => (
17 |
18 |
19 |
20 | General KPI component.
21 | +1.2k%
25 |
26 |
27 |
28 | Component to show positive change (wrapper around Kpi).
29 | +2.4k%
32 |
33 |
34 |
35 | Component to show negative change (wrapper around Kpi).
36 | -4.8k%
39 |
40 |
41 |
42 | );
43 |
44 | KpiExample.propTypes = {
45 | route: PropTypes.object.isRequired,
46 | };
47 |
48 | export default KpiExample;
49 |
--------------------------------------------------------------------------------
/src/guide/views/label/LabelExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | CheckBox,
11 | Label,
12 | SubLabel,
13 | } from '../../../framework/framework';
14 |
15 | const LabelExample = props => (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Sub-label
31 |
32 |
33 | );
34 |
35 | LabelExample.propTypes = {
36 | route: PropTypes.object.isRequired,
37 | };
38 |
39 | export default LabelExample;
40 |
--------------------------------------------------------------------------------
/src/guide/views/labeledField/LabeledFieldExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | AddOnControl,
11 | AddOnLabel,
12 | LabeledField,
13 | TextInput,
14 | } from '../../../framework/framework';
15 |
16 | const LabeledFieldExample = props => (
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 | $
33 |
34 | USD
35 |
36 |
37 |
38 |
39 |
40 | );
41 |
42 | LabeledFieldExample.propTypes = {
43 | route: PropTypes.object.isRequired,
44 | };
45 |
46 | export default LabeledFieldExample;
47 |
--------------------------------------------------------------------------------
/src/guide/views/leftFixedLayout/LeftFixedLayoutExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | LeftFixedLayout,
11 | } from '../../../framework/framework';
12 |
13 | const LeftFixedLayoutExample = props => (
14 |
15 |
16 |
17 | left
20 | )}
21 | right={(
22 |
23 | right
24 |
25 | )}
26 | />
27 |
28 |
29 |
30 | );
31 |
32 | LeftFixedLayoutExample.propTypes = {
33 | route: PropTypes.object.isRequired,
34 | };
35 |
36 | export default LeftFixedLayoutExample;
37 |
--------------------------------------------------------------------------------
/src/guide/views/notFound/NotFoundView.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | import Page from '../../components/page/Page.jsx';
5 |
6 | const NotFoundView = () => (
7 |
8 |
9 |
Page not found.
10 |
11 |
12 | );
13 |
14 | export default NotFoundView;
15 |
--------------------------------------------------------------------------------
/src/guide/views/pickedSummary/PickedSummaryExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React, {
4 | } from 'react';
5 |
6 | import Page, {
7 | Example,
8 | } from '../../components/page/Page.jsx';
9 |
10 | import {
11 | PickedSummary,
12 | } from '../../../framework/framework';
13 |
14 | const PickedListExample = (props) => {
15 | const examples = Object.keys(PickedSummary.TYPE).map(key =>
16 |
20 |
21 | {key}
22 |
23 |
24 | );
25 |
26 | return (
27 |
28 | {examples}
29 |
30 | );
31 | };
32 |
33 | PickedListExample.propTypes = {
34 | route: PropTypes.object.isRequired,
35 | };
36 |
37 | export default PickedListExample;
38 |
--------------------------------------------------------------------------------
/src/guide/views/textArea/TextAreaExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | TextArea,
11 | } from '../../../framework/framework';
12 |
13 | const TextAreaExample = props => (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 |
43 | TextAreaExample.propTypes = {
44 | route: PropTypes.object.isRequired,
45 | };
46 |
47 | export default TextAreaExample;
48 |
--------------------------------------------------------------------------------
/src/guide/views/titleBar/TitleBarExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import {
10 | CallOutButton,
11 | TitleBar,
12 | } from '../../../framework/framework';
13 |
14 | const TitleBarExample = props => (
15 |
16 |
17 |
18 |
22 | Add something
23 | ,
24 |
25 | Add something else
26 | ,
27 | ]}
28 | />
29 |
30 |
31 |
32 | );
33 |
34 | TitleBarExample.propTypes = {
35 | route: PropTypes.object.isRequired,
36 | };
37 |
38 | export default TitleBarExample;
39 |
--------------------------------------------------------------------------------
/src/guide/views/tooltip/TooltipExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | } from '../../components/page/Page.jsx';
8 |
9 | import { Tooltip } from '../../../framework/framework';
10 |
11 | const TooltipExample = props => (
12 |
13 |
14 |
15 |
19 | hover me.
20 |
21 |
22 |
23 |
24 | );
25 |
26 | TooltipExample.propTypes = {
27 | route: PropTypes.object.isRequired,
28 | };
29 |
30 | export default TooltipExample;
31 |
--------------------------------------------------------------------------------
/src/guide/views/verticalLayout/VerticalLayoutExample.jsx:
--------------------------------------------------------------------------------
1 |
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | import Page, {
6 | Example,
7 | Text,
8 | } from '../../components/page/Page.jsx';
9 |
10 | import {
11 | VerticalLayout,
12 | } from '../../../framework/framework';
13 |
14 | const LabelExample = props => (
15 |
16 |
17 |
18 | This component is responsible for positioning its children. It has
19 | no "aesthetic" styles. It enforces a regular vertical rhythm
20 | by adding a fixed amount of space between each of its children.
21 |
22 |
23 | First item
24 | Second item
25 | Third item
26 | Fourth item
27 |
28 |
29 |
30 | );
31 |
32 | LabelExample.propTypes = {
33 | route: PropTypes.object.isRequired,
34 | };
35 |
36 | export default LabelExample;
37 |
--------------------------------------------------------------------------------
/test/assets/img/testImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smaato/ui-framework/HEAD/test/assets/img/testImage.png
--------------------------------------------------------------------------------