From 024d385909fe5ddfa90d85939e47bbdaaf1f1b88 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 26 Feb 2020 15:30:28 +0530 Subject: [PATCH 01/21] feat(tabs): Initial commit --- packages/@nucleus/package.json | 1 + packages/@nucleus/tests/dummy/app/router.js | 1 + .../tests/dummy/app/templates/docs.hbs | 1 + .../templates/docs/components/nucleus-tabs.md | 72 ++++++++++++++++ packages/tabs/CHANGELOG.md | 4 + packages/tabs/LICENSE.md | 9 ++ packages/tabs/README.md | 24 ++++++ .../tabs/addon/components/nucleus-tabs.js | 31 +++++++ .../tabs/addon/components/nucleus-tabs/tab.js | 13 +++ packages/tabs/addon/constants/nucleus-tabs.js | 5 ++ packages/tabs/addon/styles/addon.scss | 3 + .../styles/components/_nucleus-tabs.scss | 3 + .../templates/components/nucleus-tabs.hbs | 0 .../templates/components/nucleus-tabs/tab.hbs | 0 packages/tabs/addon/utils/safe-set.js | 34 ++++++++ packages/tabs/app/.gitkeep | 0 packages/tabs/app/components/nucleus-tabs.js | 1 + .../tabs/app/components/nucleus-tabs/tab.js | 1 + packages/tabs/app/styles/app.scss | 3 + packages/tabs/config/deploy.js | 29 +++++++ packages/tabs/config/environment.js | 60 ++++++++++++++ packages/tabs/ember-backstop/backstop.js | 37 +++++++++ .../puppet/clickAndHoverHelper.js | 41 ++++++++++ .../engine_scripts/puppet/onReady.js | 11 +++ .../engine_scripts/puppet/overrideCSS.js | 13 +++ packages/tabs/ember-cli-build.js | 14 ++++ packages/tabs/index.js | 34 ++++++++ packages/tabs/package.json | 82 +++++++++++++++++++ packages/tabs/testem.js | 30 +++++++ packages/tabs/tests/.eslintrc.js | 5 ++ packages/tabs/tests/dummy/app/app.js | 14 ++++ .../tabs/tests/dummy/app/components/.gitkeep | 0 .../tabs/tests/dummy/app/controllers/.gitkeep | 0 .../tabs/tests/dummy/app/helpers/.gitkeep | 0 packages/tabs/tests/dummy/app/index.html | 25 ++++++ packages/tabs/tests/dummy/app/models/.gitkeep | 0 packages/tabs/tests/dummy/app/resolver.js | 3 + packages/tabs/tests/dummy/app/router.js | 12 +++ packages/tabs/tests/dummy/app/routes/.gitkeep | 0 packages/tabs/tests/dummy/app/styles/app.scss | 3 + .../tabs/tests/dummy/config/environment.js | 54 ++++++++++++ .../tests/dummy/config/optional-features.json | 3 + packages/tabs/tests/dummy/config/targets.js | 19 +++++ packages/tabs/tests/dummy/public/robots.txt | 3 + packages/tabs/tests/helpers/.gitkeep | 0 packages/tabs/tests/index.html | 33 ++++++++ packages/tabs/tests/integration/.gitkeep | 0 .../components/nucleus-tabs-test.js | 6 ++ packages/tabs/tests/test-helper.js | 8 ++ packages/tabs/tests/unit/.gitkeep | 0 50 files changed, 745 insertions(+) create mode 100644 packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md create mode 100644 packages/tabs/CHANGELOG.md create mode 100644 packages/tabs/LICENSE.md create mode 100644 packages/tabs/README.md create mode 100644 packages/tabs/addon/components/nucleus-tabs.js create mode 100644 packages/tabs/addon/components/nucleus-tabs/tab.js create mode 100644 packages/tabs/addon/constants/nucleus-tabs.js create mode 100644 packages/tabs/addon/styles/addon.scss create mode 100644 packages/tabs/addon/styles/components/_nucleus-tabs.scss create mode 100644 packages/tabs/addon/templates/components/nucleus-tabs.hbs create mode 100644 packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs create mode 100644 packages/tabs/addon/utils/safe-set.js create mode 100644 packages/tabs/app/.gitkeep create mode 100644 packages/tabs/app/components/nucleus-tabs.js create mode 100644 packages/tabs/app/components/nucleus-tabs/tab.js create mode 100644 packages/tabs/app/styles/app.scss create mode 100644 packages/tabs/config/deploy.js create mode 100644 packages/tabs/config/environment.js create mode 100644 packages/tabs/ember-backstop/backstop.js create mode 100644 packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js create mode 100644 packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/onReady.js create mode 100644 packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/overrideCSS.js create mode 100644 packages/tabs/ember-cli-build.js create mode 100644 packages/tabs/index.js create mode 100644 packages/tabs/package.json create mode 100644 packages/tabs/testem.js create mode 100644 packages/tabs/tests/.eslintrc.js create mode 100644 packages/tabs/tests/dummy/app/app.js create mode 100644 packages/tabs/tests/dummy/app/components/.gitkeep create mode 100644 packages/tabs/tests/dummy/app/controllers/.gitkeep create mode 100644 packages/tabs/tests/dummy/app/helpers/.gitkeep create mode 100644 packages/tabs/tests/dummy/app/index.html create mode 100644 packages/tabs/tests/dummy/app/models/.gitkeep create mode 100644 packages/tabs/tests/dummy/app/resolver.js create mode 100644 packages/tabs/tests/dummy/app/router.js create mode 100644 packages/tabs/tests/dummy/app/routes/.gitkeep create mode 100644 packages/tabs/tests/dummy/app/styles/app.scss create mode 100644 packages/tabs/tests/dummy/config/environment.js create mode 100644 packages/tabs/tests/dummy/config/optional-features.json create mode 100644 packages/tabs/tests/dummy/config/targets.js create mode 100644 packages/tabs/tests/dummy/public/robots.txt create mode 100644 packages/tabs/tests/helpers/.gitkeep create mode 100644 packages/tabs/tests/index.html create mode 100644 packages/tabs/tests/integration/.gitkeep create mode 100644 packages/tabs/tests/integration/components/nucleus-tabs-test.js create mode 100644 packages/tabs/tests/test-helper.js create mode 100644 packages/tabs/tests/unit/.gitkeep diff --git a/packages/@nucleus/package.json b/packages/@nucleus/package.json index c00344e9..7f1035a1 100644 --- a/packages/@nucleus/package.json +++ b/packages/@nucleus/package.json @@ -27,6 +27,7 @@ "@freshworks/inline-banner": "^0.5.0", "@freshworks/modal": "^0.5.0", "@freshworks/toast-message": "^0.6.0", + "@freshworks/tabs": "^0.1.0", "ember-cli-autoprefixer": "^0.8.1", "ember-cli-babel": "^7.11.1", "ember-cli-htmlbars": "^4.0.0", diff --git a/packages/@nucleus/tests/dummy/app/router.js b/packages/@nucleus/tests/dummy/app/router.js index 9de255c1..9823652c 100644 --- a/packages/@nucleus/tests/dummy/app/router.js +++ b/packages/@nucleus/tests/dummy/app/router.js @@ -20,6 +20,7 @@ Router.map(function() { this.route("nucleus-modal"); this.route("nucleus-toast-message"); this.route("nucleus-banner"); + this.route("nucleus-tabs"); }); this.route('not-found', { path: '/*path' }); diff --git a/packages/@nucleus/tests/dummy/app/templates/docs.hbs b/packages/@nucleus/tests/dummy/app/templates/docs.hbs index e9e64633..ec66d07d 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/docs.hbs @@ -11,6 +11,7 @@ {{nav.item "Inline Banner" "docs.components.nucleus-inline-banner"}} {{nav.item "Modal" "docs.components.nucleus-modal"}} {{nav.item "Toast message" "docs.components.nucleus-toast-message"}} + {{nav.item "Tabs" "docs.components.nucleus-tabs"}} {{/viewer.nav}} {{#viewer.main}} diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md new file mode 100644 index 00000000..2bb97178 --- /dev/null +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -0,0 +1,72 @@ +# Tabs + +```sh +yarn add @freshworks/tabs +``` + +Tabs are used to organise content under each section. Tabs are most helpful when there is a lot of content to show in a page. Tabs can help in showing content which are under the same level of hierarchy, under each section inside the same page. + +## Usage + +#### 1. Categorisation + +It's easy for the user to quickly distinguish which tab belongs to which content. + +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs.hbs"}} + {{nucleus-tabs}} + {{/demo.example}} + {{demo.snippet 'nucleus-tabs.hbs'}} +{{/docs-demo}} + + +## Guidelines + +✅**Do's** + +1. Tabs should be placed in a single row over the content + +2. Include all interactive states for the tabs + + +🚫**Dont's** + +1. Dont use tabs for sequential content. Users can navigate to any tab at any time and cannot be expected to do it sequentially. + +## Accessibility + +__role=tablist__ + +Indicates that the element serves as a container for a set of tabs. + +__aria-label=Entertainment__ + +Provides a label that describes the purpose of the set of tabs. + + +__role=tab__ + +Indicates the element serves as a tab control. + +__aria-selected=true__ + +Indicates the tab control is activated and its associated panel is displayed. + +__aria-selected=false__ + +Indicates the tab control is not active and its associated panel is NOT displayed. + +__aria-controls=IDREF__ + +Refers to the tabpanel element associated with the tab. + + +__role=tabpanel__ + +Indicates the element serves as a container for tab panel content. + +__aria-labelledby=IDREF__ + +Refers to the tab element that controls the panel. + +{{docs-note}} \ No newline at end of file diff --git a/packages/tabs/CHANGELOG.md b/packages/tabs/CHANGELOG.md new file mode 100644 index 00000000..e4d87c4d --- /dev/null +++ b/packages/tabs/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/tabs/LICENSE.md b/packages/tabs/LICENSE.md new file mode 100644 index 00000000..f8d1edb3 --- /dev/null +++ b/packages/tabs/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2019 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/tabs/README.md b/packages/tabs/README.md new file mode 100644 index 00000000..2323143b --- /dev/null +++ b/packages/tabs/README.md @@ -0,0 +1,24 @@ +@freshworks/tabs +============================================================================== + +``` +yarn add @freshworks/tabs +``` + +Tabs are used to organise content under each section. + +Scenario +------------------------------------------------------------------------------ +Tabs are most helpful when there is a lot of content to show in a page. Tabs can help in showing content which are under the same level of hierarchy, under each section inside the same page. + +Guidelines +------------------------------------------------------------------------------ +**Do's** +1. Tabs should be placed in a single row over the content +2. Include all interactive states for the tabs + + +**Dont's** +1. Dont use tabs for sequential content. Users can navigate to any tab at any time and cannot be expected to do it sequentially. + + diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js new file mode 100644 index 00000000..e57c8a6f --- /dev/null +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -0,0 +1,31 @@ +import { + classNames, + attributeBindings, + classNameBindings, + layout as templateLayout, +} from '@ember-decorators/component'; + +import Component from '@ember/component'; +import layout from "../templates/components/nucleus-tabs"; + +/** + __Usage:__ + + [Refer component page](/docs/components/nucleus-tabs) + + @class Nucleus Tab + @namespace Components + @extends Ember.Component + @public +*/ +@templateLayout(layout) +@classNames('nucleus-tabs') +@classNameBindings( + 'customClass', +) +@attributeBindings('_label:aria-label', 'autofocus') +class NucleusTabs extends Component { + +} + +export default NucleusTabs; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab.js b/packages/tabs/addon/components/nucleus-tabs/tab.js new file mode 100644 index 00000000..c4c2cf8f --- /dev/null +++ b/packages/tabs/addon/components/nucleus-tabs/tab.js @@ -0,0 +1,13 @@ +import { classNames, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; +import Component from '@ember/component'; +import layout from '../../templates/components/nucleus-tabs/tab'; + +@tagName('div') +@templateLayout(layout) +@classNames('nucleus-tabs--panel') +@classNameBindings('isActive:active') +class Tab extends Component { + +} + +export default Tab; diff --git a/packages/tabs/addon/constants/nucleus-tabs.js b/packages/tabs/addon/constants/nucleus-tabs.js new file mode 100644 index 00000000..be65a0b8 --- /dev/null +++ b/packages/tabs/addon/constants/nucleus-tabs.js @@ -0,0 +1,5 @@ +const TABS_STATE = { + +}; + +export { TABS_STATE }; \ No newline at end of file diff --git a/packages/tabs/addon/styles/addon.scss b/packages/tabs/addon/styles/addon.scss new file mode 100644 index 00000000..1cc72022 --- /dev/null +++ b/packages/tabs/addon/styles/addon.scss @@ -0,0 +1,3 @@ +@import "nucleus/variables"; +@import "nucleus/animations"; +@import "./components/nucleus-tabs"; diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss new file mode 100644 index 00000000..1782c950 --- /dev/null +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -0,0 +1,3 @@ +.nucleus-tabs { + display: flex; +} diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/addon/utils/safe-set.js b/packages/tabs/addon/utils/safe-set.js new file mode 100644 index 00000000..79c2b9fe --- /dev/null +++ b/packages/tabs/addon/utils/safe-set.js @@ -0,0 +1,34 @@ +// [TODO] Move to core if required + +import { set } from '@ember/object'; + +/** + Use this util to safely set a property in callbacks or later runs. + + ```js + import Component from '@ember/component'; + import safeSet from "../utils/safe-set"; + import { later } from "f@ember/runloop"; + export default Component.extend({ + state: null, + action: { + setFocusState() { + later(() => { + safeSet(this, 'state', 'focussed'); + }, 1000); + } + } + }); + ``` + @function safeSet + @param {object} context reference of the instance + @param {string} key name of the property that needs to be modified + @param {string} value value to be updated +*/ + + +export default function safeSet(context, key, value) { + if (!context.isDestroyed && !context.isDestroying) { + set(context, key, value); + } +} diff --git a/packages/tabs/app/.gitkeep b/packages/tabs/app/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/app/components/nucleus-tabs.js b/packages/tabs/app/components/nucleus-tabs.js new file mode 100644 index 00000000..e1f8ab61 --- /dev/null +++ b/packages/tabs/app/components/nucleus-tabs.js @@ -0,0 +1 @@ +export { default } from '@freshworks/tabs/components/nucleus-tabs'; \ No newline at end of file diff --git a/packages/tabs/app/components/nucleus-tabs/tab.js b/packages/tabs/app/components/nucleus-tabs/tab.js new file mode 100644 index 00000000..42707c99 --- /dev/null +++ b/packages/tabs/app/components/nucleus-tabs/tab.js @@ -0,0 +1 @@ +export { default } from '@freshworks/tabs/components/nucleus-tabs/tab'; \ No newline at end of file diff --git a/packages/tabs/app/styles/app.scss b/packages/tabs/app/styles/app.scss new file mode 100644 index 00000000..f281a636 --- /dev/null +++ b/packages/tabs/app/styles/app.scss @@ -0,0 +1,3 @@ +// Dummy file + +// Ember cli 3.4 expects styles/app.scss file to be found inside app folder. diff --git a/packages/tabs/config/deploy.js b/packages/tabs/config/deploy.js new file mode 100644 index 00000000..b5581aed --- /dev/null +++ b/packages/tabs/config/deploy.js @@ -0,0 +1,29 @@ +/* eslint-env node */ +'use strict'; + +module.exports = function(deployTarget) { + let ENV = { + build: {} + // include other plugin configuration that applies to all deploy targets here + }; + + if (deployTarget === 'development') { + ENV.build.environment = 'development'; + // configure other plugins for development deploy target here + } + + if (deployTarget === 'staging') { + ENV.build.environment = 'production'; + // configure other plugins for staging deploy target here + } + + if (deployTarget === 'production') { + ENV.build.environment = 'production'; + // configure other plugins for production deploy target here + } + + // Note: if you need to build some configuration asynchronously, you can return + // a promise that resolves with the ENV object instead of returning the + // ENV object synchronously. + return ENV; +}; diff --git a/packages/tabs/config/environment.js b/packages/tabs/config/environment.js new file mode 100644 index 00000000..defadf30 --- /dev/null +++ b/packages/tabs/config/environment.js @@ -0,0 +1,60 @@ +'use strict'; + +/* eslint-env node */ +module.exports = function(environment) { + let ENV = { + modulePrefix: 'nucleus', + environment, + rootURL: '/', + locationType: 'auto', + EmberENV: { + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true + }, + EXTEND_PROTOTYPES: { + // Prevent Ember Data from overriding Date.parse. + Date: false + } + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + } + }; + + ENV['ember-a11y-testing'] = { + componentOptions: { + turnAuditOff: true, // Change to true to disable the audit in development + visualNoiseLevel: 2, + axeViolationClassNames: ['alert-box', 'alert-box--a11y'] + } + } + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + ENV.APP.autoboot = false; + } + + if (environment === 'production') { + // here you can enable a production-specific feature + } + + return ENV; +}; diff --git a/packages/tabs/ember-backstop/backstop.js b/packages/tabs/ember-backstop/backstop.js new file mode 100644 index 00000000..09bad864 --- /dev/null +++ b/packages/tabs/ember-backstop/backstop.js @@ -0,0 +1,37 @@ +/* eslint-env node */ +module.exports = { + id: `ember-backstop test`, + viewports: [ + { + label: 'webview', + width: 1440, + height: 900, + }, + ], + onBeforeScript: `puppet/onBefore.js`, + onReadyScript: `puppet/onReady.js`, + scenarios: [ + { + label: '{testName}', + cookiePath: 'backstop_data/engine_scripts/cookies.json', + url: '{origin}/backstop/dview/{testId}/{scenarioId}', + delay: 500, + }, + ], + paths: { + bitmaps_reference: 'backstop_data/bitmaps_reference', + bitmaps_test: 'backstop_data/bitmaps_test', + engine_scripts: 'backstop_data/engine_scripts', + html_report: 'backstop_data/html_report', + ci_report: 'backstop_data/ci_report', + }, + report: [], + engine: 'puppet', + engineOptions: { + args: ['--no-sandbox'], + }, + asyncCaptureLimit: 10, + asyncCompareLimit: 50, + debug: false, + debugWindow: false, +}; diff --git a/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js new file mode 100644 index 00000000..d6a16e0c --- /dev/null +++ b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js @@ -0,0 +1,41 @@ +/* eslint-env browser, node */ + +module.exports = async (page, scenario) => { + const hoverSelector = scenario.hoverSelectors || scenario.hoverSelector; + const clickSelector = scenario.clickSelectors || scenario.clickSelector; + const keyPressSelector = scenario.keyPressSelectors || scenario.keyPressSelector; + const scrollToSelector = scenario.scrollToSelector; + const postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int] + + if (keyPressSelector) { + for (const keyPressSelectorItem of [].concat(keyPressSelector)) { + await page.waitFor(keyPressSelectorItem.selector); + await page.type(keyPressSelectorItem.selector, keyPressSelectorItem.keyPress); + } + } + + if (hoverSelector) { + for (const hoverSelectorIndex of [].concat(hoverSelector)) { + await page.waitFor(hoverSelectorIndex); + await page.hover(hoverSelectorIndex); + } + } + + if (clickSelector) { + for (const clickSelectorIndex of [].concat(clickSelector)) { + await page.waitFor(clickSelectorIndex); + await page.click(clickSelectorIndex); + } + } + + if (postInteractionWait) { + await page.waitFor(postInteractionWait); + } + + if (scrollToSelector) { + await page.waitFor(scrollToSelector); + await page.evaluate(scrollToSelector => { + document.querySelector(scrollToSelector).scrollIntoView(); + }, scrollToSelector); + } +}; diff --git a/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/onReady.js b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/onReady.js new file mode 100644 index 00000000..94c95fe8 --- /dev/null +++ b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/onReady.js @@ -0,0 +1,11 @@ +/* eslint-env browser, node */ + +const debug = require('debug')('BackstopJS'); + +module.exports = async (page, scenario) => { + debug('SCENARIO > ' + scenario.label); + await require('./overrideCSS')(page, scenario); + await require('./clickAndHoverHelper')(page, scenario); + + // add more ready handlers here... +}; diff --git a/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/overrideCSS.js b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/overrideCSS.js new file mode 100644 index 00000000..29cd0e20 --- /dev/null +++ b/packages/tabs/ember-backstop/backstop_data/engine_scripts/puppet/overrideCSS.js @@ -0,0 +1,13 @@ +/* eslint-env browser, node */ + +module.exports = function(page) { + // inject arbitrary css to override styles + page.evaluate(() => { + const BACKSTOP_TEST_CSS_OVERRIDE = `#ember-testing {width: 100% !important; height: 100% !important; -webkit-transform: scale(1) !important; transform: scale(1) !important;}`; + let style = document.createElement('style'); + style.type = 'text/css'; + let styleNode = document.createTextNode(BACKSTOP_TEST_CSS_OVERRIDE); + style.appendChild(styleNode); + document.head.appendChild(style); + }); +}; diff --git a/packages/tabs/ember-cli-build.js b/packages/tabs/ember-cli-build.js new file mode 100644 index 00000000..b908d9d4 --- /dev/null +++ b/packages/tabs/ember-cli-build.js @@ -0,0 +1,14 @@ +'use strict'; + +// eslint-disable-next-line node/no-unpublished-require +const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); + +module.exports = function(defaults) { + let app = new EmberAddon(defaults, + { + hinting: false + } + ); + + return app.toTree(); +}; diff --git a/packages/tabs/index.js b/packages/tabs/index.js new file mode 100644 index 00000000..5b337fac --- /dev/null +++ b/packages/tabs/index.js @@ -0,0 +1,34 @@ +/* eslint-env node */ +'use strict'; +// eslint-disable-next-line node/no-unpublished-require +const mergeTrees = require('broccoli-merge-trees'); +// eslint-disable-next-line node/no-unpublished-require +const Funnel = require('broccoli-funnel'); +const path = require('path'); + +module.exports = { + name: '@freshworks/tabs', + + isDevelopingAddon() { + return true; + }, + + included(app, parentAddon) { + let target = (parentAddon || app); + target.options = target.options || {}; + target.options.babel = target.options.babel || { includePolyfill: true }; + return this._super.included.apply(this, arguments); + }, + + treeForAddonStyles(tree) { + let coreStyleTree = new Funnel(this.getCoreStylesPath(), { + destDir: 'nucleus' + }); + return mergeTrees([coreStyleTree, tree]); + }, + + getCoreStylesPath() { + let pkgPath = path.dirname(require.resolve(`@freshworks/core/package.json`)); + return path.join(pkgPath, 'app/styles'); + } +}; diff --git a/packages/tabs/package.json b/packages/tabs/package.json new file mode 100644 index 00000000..456afef5 --- /dev/null +++ b/packages/tabs/package.json @@ -0,0 +1,82 @@ +{ + "name": "@freshworks/tabs", + "version": "0.1.0", + "description": "tabs component in Nucleus", + "keywords": [ + "ember-addon" + ], + "repository": "https://github.com/freshdesk/nucleus", + "license": "MIT", + "author": "", + "directories": { + "doc": "doc", + "test": "tests" + }, + "scripts": { + "build": "ember build", + "start": "ember serve -p 4003", + "deploy": "ember deploy production", + "test": "ember backstop-remote & COVERAGE=TRUE ember test --test-port=1509", + "posttest": "ember backstop-stop", + "test:dev": "COVERAGE=TRUE ember test --server -launch=false" + }, + "dependencies": { + "@freshworks/core": "^0.1.9", + "@freshworks/icon": "^0.5.0", + "ember-cli-babel": "^7.11.1", + "ember-cli-htmlbars": "^4.0.0", + "ember-cli-sass": "^10.0.0", + "ember-css-transitions": "^0.1.16", + "ember-decorators": "^6.1.1", + "ember-decorators-polyfill": "shibulijack-fd/ember-decorators-polyfill#master", + "ember-truth-helpers": "^2.1.0" + }, + "devDependencies": { + "@ember/optional-features": "^1.0.0", + "broccoli-asset-rev": "^3.0.0", + "broccoli-funnel": "^2.0.2", + "broccoli-merge-trees": "^3.0.2", + "ember-a11y-testing": "^1.0.0", + "ember-backstop": "^1.3.4", + "ember-cli": "~3.13.1", + "ember-cli-autoprefixer": "^0.8.1", + "ember-cli-code-coverage": "^1.0.0-beta.8", + "ember-cli-dependency-checker": "^3.1.0", + "ember-cli-deploy": "^1.0.2", + "ember-cli-deploy-build": "^1.1.1", + "ember-cli-deploy-git": "^1.3.3", + "ember-cli-deploy-git-ci": "^1.0.1", + "ember-cli-flash": "^1.7.2", + "ember-cli-inject-live-reload": "^2.0.1", + "ember-cli-sri": "^2.1.1", + "ember-cli-uglify": "^3.0.0", + "ember-disable-prototype-extensions": "^1.1.3", + "ember-export-application-global": "^2.0.0", + "ember-load-initializers": "^2.1.0", + "ember-maybe-import-regenerator": "^0.1.6", + "ember-qunit": "^4.5.1", + "ember-resolver": "^5.3.0", + "ember-sinon": "^4.0.0", + "ember-sinon-qunit": "^3.4.0", + "ember-source": "~3.13.0", + "ember-test-selectors": "^2.1.0", + "loader.js": "^4.7.0", + "qunit-dom": "^0.9.0", + "sass": "^1.23.0" + }, + "engines": { + "node": "8.* || >= 10.*" + }, + "ember-addon": { + "configPath": "tests/dummy/config", + "before": [ + "ember-cli-htmlbars", + "ember-svg-jar" + ] + }, + "publishConfig": { + "access": "public" + }, + "homepage": "https://freshdesk.github.io/nucleus", + "gitHead": "272f136386ba4d45348c6498f55a1dabe873d1fc" +} diff --git a/packages/tabs/testem.js b/packages/tabs/testem.js new file mode 100644 index 00000000..e23f1188 --- /dev/null +++ b/packages/tabs/testem.js @@ -0,0 +1,30 @@ +module.exports = { + test_page: 'tests/index.html?hidepassed', + disable_watching: true, + launch_in_ci: [ + 'Chrome' + ], + launch_in_dev: [ + 'Chrome' + ], + browser_args: { + Chrome: { + ci: [ + // --no-sandbox is needed when running Chrome inside a container + process.env.CI ? '--no-sandbox' : null, + '--headless', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--mute-audio', + '--remote-debugging-port=0', + '--window-size=1440,900' + ].filter(Boolean) + } + }, + proxies: { + '/backstop': { + target: 'http://localhost:1509', + secure: false + } + } +} diff --git a/packages/tabs/tests/.eslintrc.js b/packages/tabs/tests/.eslintrc.js new file mode 100644 index 00000000..fbf25552 --- /dev/null +++ b/packages/tabs/tests/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + embertest: true + } +}; diff --git a/packages/tabs/tests/dummy/app/app.js b/packages/tabs/tests/dummy/app/app.js new file mode 100644 index 00000000..b3b2bd67 --- /dev/null +++ b/packages/tabs/tests/dummy/app/app.js @@ -0,0 +1,14 @@ +import Application from '@ember/application'; +import Resolver from './resolver'; +import loadInitializers from 'ember-load-initializers'; +import config from './config/environment'; + +const App = Application.extend({ + modulePrefix: config.modulePrefix, + podModulePrefix: config.podModulePrefix, + Resolver +}); + +loadInitializers(App, config.modulePrefix); + +export default App; diff --git a/packages/tabs/tests/dummy/app/components/.gitkeep b/packages/tabs/tests/dummy/app/components/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/dummy/app/controllers/.gitkeep b/packages/tabs/tests/dummy/app/controllers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/dummy/app/helpers/.gitkeep b/packages/tabs/tests/dummy/app/helpers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/dummy/app/index.html b/packages/tabs/tests/dummy/app/index.html new file mode 100644 index 00000000..1f84141c --- /dev/null +++ b/packages/tabs/tests/dummy/app/index.html @@ -0,0 +1,25 @@ + + + + + + Nucleus - The Freshworks Design System + + + + {{content-for "head"}} + + + + + {{content-for "head-footer"}} + + + {{content-for "body"}} + + + + + {{content-for "body-footer"}} + + diff --git a/packages/tabs/tests/dummy/app/models/.gitkeep b/packages/tabs/tests/dummy/app/models/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/dummy/app/resolver.js b/packages/tabs/tests/dummy/app/resolver.js new file mode 100644 index 00000000..2fb563d6 --- /dev/null +++ b/packages/tabs/tests/dummy/app/resolver.js @@ -0,0 +1,3 @@ +import Resolver from 'ember-resolver'; + +export default Resolver; diff --git a/packages/tabs/tests/dummy/app/router.js b/packages/tabs/tests/dummy/app/router.js new file mode 100644 index 00000000..1202e6cf --- /dev/null +++ b/packages/tabs/tests/dummy/app/router.js @@ -0,0 +1,12 @@ +import EmberRouter from '@ember/routing/router'; +import config from './config/environment'; + +const Router = EmberRouter.extend({ + location: config.locationType, + rootURL: config.rootURL +}); + +Router.map(function() { +}); + +export default Router; \ No newline at end of file diff --git a/packages/tabs/tests/dummy/app/routes/.gitkeep b/packages/tabs/tests/dummy/app/routes/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/dummy/app/styles/app.scss b/packages/tabs/tests/dummy/app/styles/app.scss new file mode 100644 index 00000000..f281a636 --- /dev/null +++ b/packages/tabs/tests/dummy/app/styles/app.scss @@ -0,0 +1,3 @@ +// Dummy file + +// Ember cli 3.4 expects styles/app.scss file to be found inside app folder. diff --git a/packages/tabs/tests/dummy/config/environment.js b/packages/tabs/tests/dummy/config/environment.js new file mode 100644 index 00000000..3dda71a8 --- /dev/null +++ b/packages/tabs/tests/dummy/config/environment.js @@ -0,0 +1,54 @@ +'use strict'; + +/* eslint-env node */ +module.exports = function(environment) { + let ENV = { + modulePrefix: 'dummy', + environment, + rootURL: '/', + locationType: 'auto', + EmberENV: { + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true + }, + EXTEND_PROTOTYPES: { + // Prevent Ember Data from overriding Date.parse. + Date: false + } + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + } + }; + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + ENV.APP.autoboot = false; + } + + if (environment === 'production') { + // Allow ember-cli-addon-docs to update the rootURL in compiled assets + ENV.rootURL = 'ADDON_DOCS_ROOT_URL'; + // here you can enable a production-specific feature + } + + return ENV; +}; diff --git a/packages/tabs/tests/dummy/config/optional-features.json b/packages/tabs/tests/dummy/config/optional-features.json new file mode 100644 index 00000000..b1902623 --- /dev/null +++ b/packages/tabs/tests/dummy/config/optional-features.json @@ -0,0 +1,3 @@ +{ + "jquery-integration": false +} diff --git a/packages/tabs/tests/dummy/config/targets.js b/packages/tabs/tests/dummy/config/targets.js new file mode 100644 index 00000000..ebacf95b --- /dev/null +++ b/packages/tabs/tests/dummy/config/targets.js @@ -0,0 +1,19 @@ +'use strict'; + +/* eslint-env node */ +const browsers = [ + 'last 1 Chrome versions', + 'last 1 Firefox versions', + 'last 1 Safari versions' +]; + +const isCI = !!process.env.CI; +const isProduction = process.env.EMBER_ENV === 'production'; + +if (isCI || isProduction) { + browsers.push('ie 11'); +} + +module.exports = { + browsers +}; diff --git a/packages/tabs/tests/dummy/public/robots.txt b/packages/tabs/tests/dummy/public/robots.txt new file mode 100644 index 00000000..f5916452 --- /dev/null +++ b/packages/tabs/tests/dummy/public/robots.txt @@ -0,0 +1,3 @@ +# http://www.robotstxt.org +User-agent: * +Disallow: diff --git a/packages/tabs/tests/helpers/.gitkeep b/packages/tabs/tests/helpers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/index.html b/packages/tabs/tests/index.html new file mode 100644 index 00000000..5209b852 --- /dev/null +++ b/packages/tabs/tests/index.html @@ -0,0 +1,33 @@ + + + + + + Dummy Tests + + + + {{content-for "head"}} + {{content-for "test-head"}} + + + + + + {{content-for "head-footer"}} + {{content-for "test-head-footer"}} + + + {{content-for "body"}} + {{content-for "test-body"}} + + + + + + + + {{content-for "body-footer"}} + {{content-for "test-body-footer"}} + + diff --git a/packages/tabs/tests/integration/.gitkeep b/packages/tabs/tests/integration/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tabs/tests/integration/components/nucleus-tabs-test.js b/packages/tabs/tests/integration/components/nucleus-tabs-test.js new file mode 100644 index 00000000..dcf12ca9 --- /dev/null +++ b/packages/tabs/tests/integration/components/nucleus-tabs-test.js @@ -0,0 +1,6 @@ +import { module } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; + +module('Integration | Component | nucleus-tabs', function(hooks) { + setupRenderingTest(hooks); +}); diff --git a/packages/tabs/tests/test-helper.js b/packages/tabs/tests/test-helper.js new file mode 100644 index 00000000..0382a848 --- /dev/null +++ b/packages/tabs/tests/test-helper.js @@ -0,0 +1,8 @@ +import Application from '../app'; +import config from '../config/environment'; +import { setApplication } from '@ember/test-helpers'; +import { start } from 'ember-qunit'; + +setApplication(Application.create(config.APP)); + +start(); diff --git a/packages/tabs/tests/unit/.gitkeep b/packages/tabs/tests/unit/.gitkeep new file mode 100644 index 00000000..e69de29b From feac2a3002725a2038a2888549621dce1a252c81 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Thu, 27 Feb 2020 13:14:01 +0530 Subject: [PATCH 02/21] feat(tabs): Implemented {{nucleus-tabs}} Basic functionality working Refractoring some code --- .../app/components/nucleus-tabs/demo-1.js | 11 ++ .../components/nucleus-tabs/demo-1.hbs | 21 ++ .../templates/docs/components/nucleus-tabs.md | 96 +++++++++- .../tabs/addon/components/nucleus-tabs.js | 134 +++++++++++-- .../components/nucleus-tabs/tab-list-item.js | 181 ++++++++++++++++++ .../components/nucleus-tabs/tab-panel.js | 89 +++++++++ .../tabs/addon/components/nucleus-tabs/tab.js | 13 -- packages/tabs/addon/constants/nucleus-tabs.js | 11 +- .../styles/components/_nucleus-tabs.scss | 91 +++++++++ .../templates/components/nucleus-tabs.hbs | 23 +++ .../components/nucleus-tabs/tab-list-item.hbs | 1 + .../components/nucleus-tabs/tab-panel.hbs | 1 + .../templates/components/nucleus-tabs/tab.hbs | 0 .../components/nucleus-tabs/tab-list-item.js | 1 + .../nucleus-tabs/{tab.js => tab-panel.js} | 2 +- ...yle_tabs__assert0_0_document_0_webview.png | Bin 0 -> 11339 bytes ...yle_tabs__assert0_0_document_0_webview.png | Bin 0 -> 11339 bytes ...yle_tabs__assert0_0_document_0_webview.png | Bin 0 -> 11240 bytes ...led_tabs__assert0_0_document_0_webview.png | Bin 0 -> 12126 bytes .../components/nucleus-tabs-test.js | 164 ++++++++++++++++ 20 files changed, 808 insertions(+), 31 deletions(-) create mode 100644 packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js create mode 100644 packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs create mode 100644 packages/tabs/addon/components/nucleus-tabs/tab-list-item.js create mode 100644 packages/tabs/addon/components/nucleus-tabs/tab-panel.js delete mode 100644 packages/tabs/addon/components/nucleus-tabs/tab.js create mode 100644 packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs create mode 100644 packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs delete mode 100644 packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs create mode 100644 packages/tabs/app/components/nucleus-tabs/tab-list-item.js rename packages/tabs/app/components/nucleus-tabs/{tab.js => tab-panel.js} (83%) create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_background_style_tabs__assert0_0_document_0_webview.png create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_border_style_tabs__assert0_0_document_0_webview.png create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_default_style_tabs__assert0_0_document_0_webview.png create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_disabled_tabs__assert0_0_document_0_webview.png diff --git a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js new file mode 100644 index 00000000..3807c9a5 --- /dev/null +++ b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js @@ -0,0 +1,11 @@ +// BEGIN-SNIPPET tabs-component.js +import Component from '@ember/component'; + +export default Component.extend({ + actions: { + onChange(name) { + alert(`Custom action invoked. '${name}' tab selected!`) + } + } +}); +// END-SNIPPET \ No newline at end of file diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs new file mode 100644 index 00000000..65c9a732 --- /dev/null +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs @@ -0,0 +1,21 @@ +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs-2.hbs" }} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + onChange=(action "onChange") as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + {{/demo.example}} + + {{demo.snippet "nucleus-tabs-2.hbs"}} + {{demo.snippet "modal-component.js" label="component.js"}} +{{/docs-demo}} \ No newline at end of file diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index 2bb97178..6c7ef183 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -14,25 +14,115 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#docs-demo as |demo|}} {{#demo.example name="nucleus-tabs.hbs"}} - {{nucleus-tabs}} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + variant="default" as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} {{/demo.example}} {{demo.snippet 'nucleus-tabs.hbs'}} {{/docs-demo}} +#### 2. Custom actions on changing tab + +{{nucleus-tabs/demo-1}} + +#### 3. Disabled tab + +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs-3.hbs"}} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + variant="default" as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" disabled="true" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + {{/demo.example}} + {{demo.snippet 'nucleus-tabs-3.hbs'}} +{{/docs-demo}} + +## Styles + +#### 1. Default + +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs-variant1.hbs"}} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + variant="default" as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + {{/demo.example}} + {{demo.snippet 'nucleus-tabs-variant1.hbs'}} +{{/docs-demo}} + + + +#### 2. With Background +Pass 'variant' property as 'background'. + +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs-variant2.hbs"}} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + variant="background" as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + {{/demo.example}} + {{demo.snippet 'nucleus-tabs-variant2.hbs'}} +{{/docs-demo}} + ## Guidelines -✅**Do's** +✅ **Do's** 1. Tabs should be placed in a single row over the content 2. Include all interactive states for the tabs -🚫**Dont's** +🚫 **Dont's** 1. Dont use tabs for sequential content. Users can navigate to any tab at any time and cannot be expected to do it sequentially. +2. Dont use tabs for content in different levels of hierarchy. + ## Accessibility __role=tablist__ diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index e57c8a6f..b9e43568 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -1,11 +1,9 @@ -import { - classNames, - attributeBindings, - classNameBindings, - layout as templateLayout, -} from '@ember-decorators/component'; - +import { classNames, classNameBindings, layout as templateLayout } from '@ember-decorators/component'; +import { A } from '@ember/array'; +import defaultProp from '@freshworks/core/utils/default-decorator'; import Component from '@ember/component'; +import { set, computed, action } from '@ember/object'; +import { oneWay }from '@ember/object/computed'; import layout from "../templates/components/nucleus-tabs"; /** @@ -20,12 +18,126 @@ import layout from "../templates/components/nucleus-tabs"; */ @templateLayout(layout) @classNames('nucleus-tabs') -@classNameBindings( - 'customClass', -) -@attributeBindings('_label:aria-label', 'autofocus') +@classNameBindings('variantClass') class NucleusTabs extends Component { + /** + * Description : to add aria label + * + * @field description + * @type string|null + * @default null + * @readonly + * @public + */ + @defaultProp + description = null; + + /** + * selected : default open tab + * + * @field selected + * @type string|null + * @default null + * @readonly + * @public + */ + @defaultProp + selected = null; + + /** + * variant: tab styles, line/background + * + * @field variant + * @type string + * @default 'line' + * @readonly + * @public + */ + @defaultProp + variant = "line"; + + /** + * tabPanels: Collection of all tab panels + * + * @field tabPanels + * @type Array + * @public + */ + tabPanels = A([]); + + /** + * tabListItems: Collection of all tab list items + * + * @field tabListItems + * @type Array + * @public + */ + tabListItems = A([]); + + /** + * default + * + * @field default : takes intial value from selected + * @type string|null + * @public + */ + @oneWay("selected") + default; + + /** + * variantClass + * + * @field variantClass + * @type string + * @public + */ + @computed('variant', function() { + return "nucleus-tabs--" + this.variant; + }) + variantClass; + + /** + * registerPanel + * + * @method registerPanel + * @param {Object} tab + * @public + * + */ + @action + registerPanel(tab) { + this.get('tabPanels').pushObject(tab); + } + + /** + * registerTabListItem + * + * @method registerTabListItem + * @param {Object} tab + * @public + * + */ + @action + registerTabListItem(tabList) { + this.get('tabListItems').pushObject(tabList); + } + /** + * activateTab : Handler that will be called when a tab is clicked + * + * @method activateTab + * @param {string} name + * @param {any} event + * @public + * + */ + @action + activateTab(name, event) { + set(this, 'default', name); + if(this.onChange) { + this.onChange(name, event); + } + } } export default NucleusTabs; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js new file mode 100644 index 00000000..f0da4c13 --- /dev/null +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -0,0 +1,181 @@ +import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import defaultProp from '@freshworks/core/utils/default-decorator'; +import { once } from '@ember/runloop'; +import layout from '../../templates/components/nucleus-tabs/tab-list-item'; +import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' + +@tagName('button') +@templateLayout(layout) +@classNames('nucleus-tabs--list--item') +@classNameBindings('isActive:active') +@classNameBindings('isDisabled:disabled') +@attributeBindings('tabindex') +@attributeBindings('role') +@attributeBindings('aria-controls') +@attributeBindings('aria-selected') +class TabListItem extends Component { + + /** + * disabled + * + * @field disabled + * @type string + * @default 'false' + * @readonly + * @public + */ + @defaultProp + disabled = "false"; + + /** + * controls : idref to what panel this tab item controls + * + * @field controls + * @type string|null + * @default null + * @readonly + * @public + */ + @defaultProp + controls; + + /** + * role + * + * @field role + * @type string + * @default 'tab' + * @public + */ + role = "tab"; + + /** + * tabindex + * + * @field tabindex + * @type string|null + * @public + */ + @computed('index', function() { + return (this.index == 0)? null : '-1'; + }) + tabindex; + + /** + * isActive + * + * @field isActive + * @type boolean + * @public + */ + @computed('default', function() { + return (this.default === this.name); + }) + isActive; + + /** + * isDisabled + * + * @field isDisabled + * @type boolean + * @public + */ + @computed('disabled', function() { + return (this.disabled === "true")? true : false; + }) + isDisabled; + + /** + * aria-controls + * + * @field aria-controls + * @type string + * @public + */ + @computed('controls', function() { + return this.controls; + }) + "aria-controls"; + + /** + * aria-selected + * + * @field aria-selected + * @type boolean + * @public + */ + @computed('default', function() { + return (this.default === this.name).toString(); + }) + "aria-selected"; + + /** + * init : lifecycle event + * + * @method init + * @public + * + */ + init() { + super.init(...arguments); + once(this, this.registerTabListItem, { + id: this.elementId, + name: this.name + }); + } + + /** + * click : event handler + * + * @method click + * @public + * + */ + click(event) { + if(this.disabled === "false") { + this.handleActivateTab(this.name, event); + } + event.target.focus(); + } + + /** + * keyDown : event handler + * + * @method keyDown + * @public + * + */ + keyDown(event) { + event.stopPropagation(); + let target = event.target; + let nextSibling = target.nextElementSibling; + let previousSibling = target.previousElementSibling; + let firstElement = target.parentElement.firstElementChild; + let lastElement = target.parentElement.lastElementChild; + + const keyCode = TABS_KEY_CODE; + switch (event.keyCode) { + case (keyCode.ENTER || keyCode.SPACE): + target.click(); + break; + case keyCode.END: + lastElement.focus(); + break; + case keyCode.HOME: + firstElement.focus(); + break; + case keyCode.LEFT: + (previousSibling)? previousSibling.focus() : lastElement.focus(); + break; + case keyCode.RIGHT: + (nextSibling)? nextSibling.focus() : firstElement.focus(); + break; + default: + break; + } + } +} + +export default TabListItem; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js new file mode 100644 index 00000000..59025d93 --- /dev/null +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -0,0 +1,89 @@ +import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import layout from '../../templates/components/nucleus-tabs/tab-panel'; +import { once } from '@ember/runloop'; +import defaultProp from '@freshworks/core/utils/default-decorator'; + +@tagName('div') +@templateLayout(layout) +@classNames('nucleus-tabs--panel') +@classNameBindings('isActive:active') +@attributeBindings('tabindex') +@attributeBindings('role') +@attributeBindings('aria-labelledby') +class TabPanel extends Component { + + /** + * disabled + * + * @field disabled + * @type string + * @default 'false' + * @readonly + * @public + */ + @defaultProp + disabled = "false"; + + /** + * tabindex + * + * @field tabindex + * @type String + * @public + */ + tabindex = "0"; + + /** + * role + * + * @field role + * @type String + * @public + */ + role = "tabpanel" + + /** + * isActive + * + * @field isActive + * @type Boolean + * @public + */ + @computed('props.[]', function() { + return (this.props.default === this.name); + }) + isActive; + + /** + * isActive + * + * @field isActive + * @type Boolean + * @public + */ + @computed('props.tabList.[]', function() { + let tabList = this.props.tabListItems.findBy('name', this.name); + return (tabList)? tabList.id : ""; + }) + "aria-labelledby"; + + /** + * init : lifecycle event + * + * @method init + * @public + * + */ + init() { + super.init(...arguments); + once(this, this.props.registerPanel, { + id: this.elementId, + name: this.name, + disabled: this.disabled + }); + } +} + +export default TabPanel; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab.js b/packages/tabs/addon/components/nucleus-tabs/tab.js deleted file mode 100644 index c4c2cf8f..00000000 --- a/packages/tabs/addon/components/nucleus-tabs/tab.js +++ /dev/null @@ -1,13 +0,0 @@ -import { classNames, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; -import Component from '@ember/component'; -import layout from '../../templates/components/nucleus-tabs/tab'; - -@tagName('div') -@templateLayout(layout) -@classNames('nucleus-tabs--panel') -@classNameBindings('isActive:active') -class Tab extends Component { - -} - -export default Tab; diff --git a/packages/tabs/addon/constants/nucleus-tabs.js b/packages/tabs/addon/constants/nucleus-tabs.js index be65a0b8..7ada4067 100644 --- a/packages/tabs/addon/constants/nucleus-tabs.js +++ b/packages/tabs/addon/constants/nucleus-tabs.js @@ -1,5 +1,10 @@ -const TABS_STATE = { - +const TABS_KEY_CODE = { + ENTER: 13, + SPACE: 32, + END: 35, + HOME: 36, + LEFT: 37, + RIGHT: 39 }; -export { TABS_STATE }; \ No newline at end of file +export { TABS_KEY_CODE }; \ No newline at end of file diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 1782c950..7dc46d17 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -1,3 +1,94 @@ .nucleus-tabs { display: flex; + flex-direction: column; + + &--list { + width: 100%; + display: flex; + flex-wrap: nowrap; + padding: 0 12px; + border-bottom: 1px solid $color-smoke-50; + border-radius: 4px; + + &--item { + all: unset; + flex-shrink: 0; + position: relative; + text-align: center; + line-height: 20px; + padding: 10px 8px; + margin: 0 4px; + font-size: 14px; + color: $color-smoke-700; + + &::after { + content: ''; + position: absolute; + left: 0; + width: 100%; + bottom: -1px; + box-sizing: border-box; + } + + &:hover { + outline: none; + border: 0; + box-shadow: none; + cursor: pointer; + + &::after { + border-bottom: 2px solid $color-smoke-100; + } + } + + &:focus { + outline: none; + border: 0; + box-shadow: none; + + &::after { + height: calc(100% + 2px); + border: 2px solid $color-azure-800; + border-radius: 4px; + } + } + } + + &--item.disabled { + color: $color-smoke-300; + + &:hover { + cursor: default; + + &::after { + display: none; + } + } + } + + &--item.active { + color: $color-azure-800; + + &::after { + border-bottom: 2px solid $color-azure-800; + } + } + } + + &--panel { + width: 100%; + display: none; + padding: 8px 20px; + } + + &--panel.active { + display: block; + } + + &--background { + .nucleus-tabs--list { + background: $color-smoke-25; + border: 1px solid $color-smoke-50; + } + } } diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index e69de29b..10d8ada5 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -0,0 +1,23 @@ +
+ {{#each this.tabPanels key="id" as |tab index|}} + {{#nucleus-tabs/tab-list-item + default=default + name=tab.name + disabled=tab.disabled + index=index + controls=tab.id + registerTabListItem=(action registerTabListItem) + handleActivateTab=(action activateTab)}} + {{tab.name}} + {{/nucleus-tabs/tab-list-item}} + {{/each}} +
+ +{{yield (hash + panel=(component "nucleus-tabs/tab-panel") + props=(hash + registerPanel=(action "registerPanel") + default=default + tabListItems=tabListItems + ) +)}} diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs new file mode 100644 index 00000000..fb5c4b15 --- /dev/null +++ b/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs @@ -0,0 +1 @@ +{{yield}} \ No newline at end of file diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs new file mode 100644 index 00000000..fb5c4b15 --- /dev/null +++ b/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs @@ -0,0 +1 @@ +{{yield}} \ No newline at end of file diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab.hbs deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/tabs/app/components/nucleus-tabs/tab-list-item.js b/packages/tabs/app/components/nucleus-tabs/tab-list-item.js new file mode 100644 index 00000000..da747983 --- /dev/null +++ b/packages/tabs/app/components/nucleus-tabs/tab-list-item.js @@ -0,0 +1 @@ +export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-list-item'; \ No newline at end of file diff --git a/packages/tabs/app/components/nucleus-tabs/tab.js b/packages/tabs/app/components/nucleus-tabs/tab-panel.js similarity index 83% rename from packages/tabs/app/components/nucleus-tabs/tab.js rename to packages/tabs/app/components/nucleus-tabs/tab-panel.js index 42707c99..bd5a8ea8 100644 --- a/packages/tabs/app/components/nucleus-tabs/tab.js +++ b/packages/tabs/app/components/nucleus-tabs/tab-panel.js @@ -1 +1 @@ -export { default } from '@freshworks/tabs/components/nucleus-tabs/tab'; \ No newline at end of file +export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-panel'; \ No newline at end of file diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_background_style_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_background_style_tabs__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb77a3a63b4d6adedd30dfe928e3a25f5c79e38 GIT binary patch literal 11339 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 AC;$Ke literal 0 HcmV?d00001 diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_border_style_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_border_style_tabs__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb77a3a63b4d6adedd30dfe928e3a25f5c79e38 GIT binary patch literal 11339 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 AC;$Ke literal 0 HcmV?d00001 diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_default_style_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_default_style_tabs__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..aff3e31d8abc05f3d26bad5e3a97c0ce5f530b53 GIT binary patch literal 11240 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 Si=RS;L0ojYQ1kWmpZ*0uC}0Hu literal 0 HcmV?d00001 diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_disabled_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_disabled_tabs__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..65fa22562a47c162f1579c697eea251c93f7086a GIT binary patch literal 12126 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 ZThis is the home section + {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props }} +
This is about us section
+ {{/tabs.panel}} + {{#tabs.panel name="contact" props=tabs.props }} +
This is the contact section
+ {{/tabs.panel}} + {{/nucleus-tabs}} +`; module('Integration | Component | nucleus-tabs', function(hooks) { setupRenderingTest(hooks); + + hooks.beforeEach(function() { + this.actions = {}; + this.send = (actionName, ...args) => this.actions[actionName].apply(this, args); + }); + + test('it should yield tab-list items and tab-panels', async function(assert) { + await render(sampleTabsTemplate); + assert.dom('.nucleus-tabs').exists({ count: 1 }, 'Tabs component exists.'); + assert.dom('.nucleus-tabs .nucleus-tabs--list').exists({ count: 1 }, 'Tabs component has a Tab list'); + assert.dom('.nucleus-tabs .nucleus-tabs--list--item').exists({ count: 3 }, 'Tabs component has 3 Tab list items'); + assert.dom('.nucleus-tabs .nucleus-tabs--panel').exists({ count: 3 }, 'Tabs component has right number of Tab panels'); + }); + + test('it should have only selected panel as active', async function(assert) { + await render(sampleTabsTemplate); + assert.dom('.nucleus-tabs .nucleus-tabs--panel.active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs--panel.active').hasText('This is the home section'); + }); + + test('it should have only selected tab list item as active', async function(assert) { + await render(sampleTabsTemplate); + assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').hasText('home'); + }); + + test('it should attach appropriate background class for the background variant', async function(assert) { + await render(sampleTabsTemplate); + assert.dom('.nucleus-tabs.nucleus-tabs--background').exists({ count: 1 }, 'Has one appropriate class when passing variant as prop'); + }); + + test('it should attach appropriate line class for the line variant', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + assert.dom('.nucleus-tabs.nucleus-tabs--line').exists({ count: 1 }, 'Has line class when no variant passed as prop'); + }); + + test('it should attach disable class to disabled tab list item', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props disabled="true" }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + assert.dom('.nucleus-tabs .nucleus-tabs--list--item.disabled').exists({ count: 1 }, 'Has disabled class when disabled prop is passed'); + }); + + test('it should not enable tab when tab list item is disabled', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props disabled="true" }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').hasText('home'); + }); + + test('it should yeilds onchange action', async function(assert) { + let onchangeAction = this.spy(); + this.actions.onChange = onchangeAction; + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" onChange=(action "onChange") as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props }} +
This is the about section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + + await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + assert.ok(onchangeAction.calledOnce, 'onChange action has been called.'); + }); + + test('it has accessibility attributes', async function(assert) { + await render(sampleTabsTemplate); + assert.dom('.nucleus-tabs .nucleus-tabs--list').hasAttribute('role', 'tablist'); + assert.dom('.nucleus-tabs .nucleus-tabs--list').hasAttribute('aria-label', 'site-navigation'); + assert.dom('.nucleus-tabs .nucleus-tabs--list button').hasAttribute('role', 'tab'); + assert.dom('.nucleus-tabs .nucleus-tabs--list button.active').hasAttribute('aria-selected', 'true'); + assert.dom('.nucleus-tabs .nucleus-tabs--list button:not(.active)').hasAttribute('aria-selected', 'false'); + assert.dom('.nucleus-tabs .nucleus-tabs--list button').hasAttribute('aria-controls'); + assert.dom('.nucleus-tabs .nucleus-tabs--panel').hasAttribute('role', 'tabpanel'); + assert.dom('.nucleus-tabs .nucleus-tabs--panel').hasAttribute('aria-labelledby'); + }); + + test('it passes a11y tests', async function(assert) { + await render(sampleTabsTemplate); + + return a11yAudit(this.element).then(() => { + assert.ok(true, 'no a11y errors found!'); + }); + }); + + test('visual regression for default style tabs', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); + }); + + test('visual regression for background style tabs', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" variant="background" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); + }); + + test('visual regression for disabled tabs', async function(assert) { + await render(hbs` + {{#nucleus-tabs description="site-navigation" variant="background" selected="home" as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props disabled="true" }} +
This is the home section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); + }); + }); From 5a84fa750261113767bd2dfa5f4fab84a1b2ebf0 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Tue, 3 Mar 2020 16:00:23 +0530 Subject: [PATCH 03/21] refactor(tabs): calling component attributes through get call --- .../tabs/addon/components/nucleus-tabs.js | 12 ++++---- .../components/nucleus-tabs/tab-list-item.js | 28 +++++++++---------- .../components/nucleus-tabs/tab-panel.js | 21 +++++++------- .../styles/components/_nucleus-tabs.scss | 18 +++++++----- .../templates/components/nucleus-tabs.hbs | 26 +++++++++-------- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index b9e43568..0537ce40 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -2,7 +2,7 @@ import { classNames, classNameBindings, layout as templateLayout } from '@ember- import { A } from '@ember/array'; import defaultProp from '@freshworks/core/utils/default-decorator'; import Component from '@ember/component'; -import { set, computed, action } from '@ember/object'; +import { set, get, computed, action } from '@ember/object'; import { oneWay }from '@ember/object/computed'; import layout from "../templates/components/nucleus-tabs"; @@ -54,7 +54,7 @@ class NucleusTabs extends Component { * @public */ @defaultProp - variant = "line"; + variant = 'line'; /** * tabPanels: Collection of all tab panels @@ -81,7 +81,7 @@ class NucleusTabs extends Component { * @type string|null * @public */ - @oneWay("selected") + @oneWay('selected') default; /** @@ -92,7 +92,7 @@ class NucleusTabs extends Component { * @public */ @computed('variant', function() { - return "nucleus-tabs--" + this.variant; + return 'nucleus-tabs--' + get(this, 'variant'); }) variantClass; @@ -106,7 +106,7 @@ class NucleusTabs extends Component { */ @action registerPanel(tab) { - this.get('tabPanels').pushObject(tab); + get(this, 'tabPanels').pushObject(tab); } /** @@ -119,7 +119,7 @@ class NucleusTabs extends Component { */ @action registerTabListItem(tabList) { - this.get('tabListItems').pushObject(tabList); + get(this, 'tabListItems').pushObject(tabList); } /** diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index f0da4c13..5f086ca8 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -1,6 +1,6 @@ import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { once } from '@ember/runloop'; import layout from '../../templates/components/nucleus-tabs/tab-list-item'; @@ -27,7 +27,7 @@ class TabListItem extends Component { * @public */ @defaultProp - disabled = "false"; + disabled = 'false'; /** * controls : idref to what panel this tab item controls @@ -49,7 +49,7 @@ class TabListItem extends Component { * @default 'tab' * @public */ - role = "tab"; + role = 'tab'; /** * tabindex @@ -59,7 +59,7 @@ class TabListItem extends Component { * @public */ @computed('index', function() { - return (this.index == 0)? null : '-1'; + return (get(this, 'index') === 0)? null : '-1'; }) tabindex; @@ -71,7 +71,7 @@ class TabListItem extends Component { * @public */ @computed('default', function() { - return (this.default === this.name); + return (get(this, 'default') === get(this, 'name')); }) isActive; @@ -83,7 +83,7 @@ class TabListItem extends Component { * @public */ @computed('disabled', function() { - return (this.disabled === "true")? true : false; + return (get(this, 'disabled') === 'true')? true : false; }) isDisabled; @@ -95,7 +95,7 @@ class TabListItem extends Component { * @public */ @computed('controls', function() { - return this.controls; + return get(this, 'controls'); }) "aria-controls"; @@ -107,7 +107,7 @@ class TabListItem extends Component { * @public */ @computed('default', function() { - return (this.default === this.name).toString(); + return (get(this, 'default') === get(this, 'name')).toString(); }) "aria-selected"; @@ -120,9 +120,9 @@ class TabListItem extends Component { */ init() { super.init(...arguments); - once(this, this.registerTabListItem, { - id: this.elementId, - name: this.name + once(this, get(this, 'registerTabListItem'), { + id: get(this, 'elementId'), + name: get(this, 'name') }); } @@ -134,10 +134,10 @@ class TabListItem extends Component { * */ click(event) { - if(this.disabled === "false") { - this.handleActivateTab(this.name, event); - } event.target.focus(); + if(get(this, 'disabled') === 'false') { + this.handleActivateTab(get(this, 'name'), event); + } } /** diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 59025d93..428060b2 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -1,6 +1,6 @@ import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { get, computed } from '@ember/object'; import layout from '../../templates/components/nucleus-tabs/tab-panel'; import { once } from '@ember/runloop'; import defaultProp from '@freshworks/core/utils/default-decorator'; @@ -24,7 +24,7 @@ class TabPanel extends Component { * @public */ @defaultProp - disabled = "false"; + disabled = 'false'; /** * tabindex @@ -33,7 +33,7 @@ class TabPanel extends Component { * @type String * @public */ - tabindex = "0"; + tabindex = '0'; /** * role @@ -42,7 +42,7 @@ class TabPanel extends Component { * @type String * @public */ - role = "tabpanel" + role = 'tabpanel' /** * isActive @@ -52,7 +52,7 @@ class TabPanel extends Component { * @public */ @computed('props.[]', function() { - return (this.props.default === this.name); + return (get(this.props, 'default') === get(this, 'name')); }) isActive; @@ -64,7 +64,8 @@ class TabPanel extends Component { * @public */ @computed('props.tabList.[]', function() { - let tabList = this.props.tabListItems.findBy('name', this.name); + let tabListItems = get(this.props, 'tabListItems'); + let tabList = tabListItems.findBy('name', get(this, 'name')); return (tabList)? tabList.id : ""; }) "aria-labelledby"; @@ -78,10 +79,10 @@ class TabPanel extends Component { */ init() { super.init(...arguments); - once(this, this.props.registerPanel, { - id: this.elementId, - name: this.name, - disabled: this.disabled + once(this, get(this.props, 'registerPanel'), { + id: get(this, 'elementId'), + name: get(this, 'name'), + disabled: get(this, 'disabled') }); } } diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 7dc46d17..90f8f376 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -4,11 +4,15 @@ &--list { width: 100%; - display: flex; - flex-wrap: nowrap; - padding: 0 12px; - border-bottom: 1px solid $color-smoke-50; - border-radius: 4px; + overflow: scroll hidden; + + &--container { + display: flex; + flex-wrap: nowrap; + width: 100%; + padding: 0 12px; + border-bottom: 1px solid $color-smoke-50; + } &--item { all: unset; @@ -25,8 +29,8 @@ content: ''; position: absolute; left: 0; - width: 100%; bottom: -1px; + width: 100%; box-sizing: border-box; } @@ -47,7 +51,7 @@ box-shadow: none; &::after { - height: calc(100% + 2px); + height: calc(100%); border: 2px solid $color-azure-800; border-radius: 4px; } diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index 10d8ada5..03d81ff1 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -1,16 +1,18 @@
- {{#each this.tabPanels key="id" as |tab index|}} - {{#nucleus-tabs/tab-list-item - default=default - name=tab.name - disabled=tab.disabled - index=index - controls=tab.id - registerTabListItem=(action registerTabListItem) - handleActivateTab=(action activateTab)}} - {{tab.name}} - {{/nucleus-tabs/tab-list-item}} - {{/each}} +
+ {{#each this.tabPanels key="id" as |tab index|}} + {{#nucleus-tabs/tab-list-item + default=default + name=tab.name + disabled=tab.disabled + index=index + controls=tab.id + registerTabListItem=(action registerTabListItem) + handleActivateTab=(action activateTab)}} + {{tab.name}} + {{/nucleus-tabs/tab-list-item}} + {{/each}} +
{{yield (hash From 6df0f4892384c6e3677e485f537bfefb3cd6a764 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Tue, 3 Mar 2020 16:45:27 +0530 Subject: [PATCH 04/21] fix(tabs): Background variant border radius issue --- packages/tabs/addon/styles/components/_nucleus-tabs.scss | 3 ++- packages/tabs/addon/templates/components/nucleus-tabs.hbs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 90f8f376..a4055cc8 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -51,7 +51,7 @@ box-shadow: none; &::after { - height: calc(100%); + height: calc(100% + 1px); border: 2px solid $color-azure-800; border-radius: 4px; } @@ -93,6 +93,7 @@ .nucleus-tabs--list { background: $color-smoke-25; border: 1px solid $color-smoke-50; + border-radius: 4px; } } } diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index 03d81ff1..22822589 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -1,12 +1,12 @@
- {{#each this.tabPanels key="id" as |tab index|}} + {{#each tabPanels key="id" as |tab index|}} {{#nucleus-tabs/tab-list-item - default=default name=tab.name disabled=tab.disabled - index=index controls=tab.id + index=index + default=default registerTabListItem=(action registerTabListItem) handleActivateTab=(action activateTab)}} {{tab.name}} From f590917799b4420b75ef2c46c0ae2a0a192394ee Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Tue, 3 Mar 2020 16:55:52 +0530 Subject: [PATCH 05/21] refractor(tabs): current selected name change --- packages/tabs/addon/components/nucleus-tabs.js | 6 +++--- .../tabs/addon/components/nucleus-tabs/tab-list-item.js | 8 ++++---- packages/tabs/addon/components/nucleus-tabs/tab-panel.js | 2 +- packages/tabs/addon/templates/components/nucleus-tabs.hbs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 0537ce40..0c597e56 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -77,12 +77,12 @@ class NucleusTabs extends Component { /** * default * - * @field default : takes intial value from selected + * @field currentSelected : takes intial value from selected * @type string|null * @public */ @oneWay('selected') - default; + currentSelected; /** * variantClass @@ -133,7 +133,7 @@ class NucleusTabs extends Component { */ @action activateTab(name, event) { - set(this, 'default', name); + set(this, 'currentSelected', name); if(this.onChange) { this.onChange(name, event); } diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 5f086ca8..fb538d86 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -70,8 +70,8 @@ class TabListItem extends Component { * @type boolean * @public */ - @computed('default', function() { - return (get(this, 'default') === get(this, 'name')); + @computed('currentSelected', function() { + return (get(this, 'currentSelected') === get(this, 'name')); }) isActive; @@ -106,8 +106,8 @@ class TabListItem extends Component { * @type boolean * @public */ - @computed('default', function() { - return (get(this, 'default') === get(this, 'name')).toString(); + @computed('currentSelected', function() { + return (get(this, 'currentSelected') === get(this, 'name')).toString(); }) "aria-selected"; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 428060b2..2adf2529 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -52,7 +52,7 @@ class TabPanel extends Component { * @public */ @computed('props.[]', function() { - return (get(this.props, 'default') === get(this, 'name')); + return (get(this.props, 'currentSelected') === get(this, 'name')); }) isActive; diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index 22822589..ac1ca745 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -6,7 +6,7 @@ disabled=tab.disabled controls=tab.id index=index - default=default + currentSelected=currentSelected registerTabListItem=(action registerTabListItem) handleActivateTab=(action activateTab)}} {{tab.name}} @@ -19,7 +19,7 @@ panel=(component "nucleus-tabs/tab-panel") props=(hash registerPanel=(action "registerPanel") - default=default + currentSelected=currentSelected tabListItems=tabListItems ) )}} From 17dcb82bdd8b8051ca7ba21ec44d2fc738a7b790 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 4 Mar 2020 11:36:16 +0530 Subject: [PATCH 06/21] fix(tabs): no focus on disabled button --- .../components/nucleus-tabs/tab-list-item.js | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index fb538d86..d0f0f1d1 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -15,6 +15,7 @@ import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @attributeBindings('role') @attributeBindings('aria-controls') @attributeBindings('aria-selected') +@attributeBindings('isDisabled:disabled') class TabListItem extends Component { /** @@ -27,7 +28,7 @@ class TabListItem extends Component { * @public */ @defaultProp - disabled = 'false'; + disabled = null; /** * controls : idref to what panel this tab item controls @@ -150,8 +151,6 @@ class TabListItem extends Component { keyDown(event) { event.stopPropagation(); let target = event.target; - let nextSibling = target.nextElementSibling; - let previousSibling = target.previousElementSibling; let firstElement = target.parentElement.firstElementChild; let lastElement = target.parentElement.lastElementChild; @@ -167,15 +166,41 @@ class TabListItem extends Component { firstElement.focus(); break; case keyCode.LEFT: - (previousSibling)? previousSibling.focus() : lastElement.focus(); + this.focusPreviousTab(target); break; case keyCode.RIGHT: - (nextSibling)? nextSibling.focus() : firstElement.focus(); + this.focusNextTab(target); break; default: break; } } + + focusNextTab(element, elementInFocus) { + let nextElement = element.nextElementSibling; + nextElement = (nextElement)? nextElement : element.parentElement.firstElementChild; + + if(elementInFocus && (elementInFocus.id === nextElement.id)) { + return; + } else if(nextElement.disabled) { + get(this, 'focusNextTab').call(this, nextElement, element); + } else { + nextElement.focus(); + } + } + + focusPreviousTab(element, elementInFocus) { + let previousElement = element.previousElementSibling; + previousElement = (previousElement)? previousElement : element.parentElement.lastElementChild; + + if(elementInFocus && (elementInFocus.id === previousElement.id)) { + return; + } else if(previousElement.disabled) { + get(this, 'focusPreviousTab').call(this, previousElement, element); + } else { + previousElement.focus(); + } + } } export default TabListItem; From 907fda0e6a2ff0b56ca37d07c1e1edcbaa846988 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 4 Mar 2020 11:47:03 +0530 Subject: [PATCH 07/21] fix(tabs): no focus on disabled button & safari text rendering issue --- packages/tabs/addon/styles/components/_nucleus-tabs.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index a4055cc8..8dedad32 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -60,6 +60,7 @@ &--item.disabled { color: $color-smoke-300; + -webkit-text-fill-color: $color-smoke-300; &:hover { cursor: default; From d6015a8c8ba35b08178b21ecb2e0d275f88965d8 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Thu, 5 Mar 2020 11:34:03 +0530 Subject: [PATCH 08/21] feat(tabs): 'beforeChange' handler implementation --- .../app/components/nucleus-tabs/demo-1.js | 12 +++- .../app/components/nucleus-tabs/demo-2.js | 29 ++++++++++ .../components/nucleus-tabs/demo-1.hbs | 2 +- .../components/nucleus-tabs/demo-2.hbs | 22 ++++++++ .../templates/docs/components/nucleus-tabs.md | 12 ++-- .../tabs/addon/components/nucleus-tabs.js | 15 +++-- ...yle_tabs__assert0_0_document_0_webview.png | Bin 11339 -> 11331 bytes ...led_tabs__assert0_0_document_0_webview.png | Bin 12126 -> 12117 bytes .../components/nucleus-tabs-test.js | 52 ++++++++++++++++-- 9 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js create mode 100644 packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs diff --git a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js index 3807c9a5..856eecc3 100644 --- a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js +++ b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js @@ -1,10 +1,16 @@ -// BEGIN-SNIPPET tabs-component.js +// BEGIN-SNIPPET nucleus-tabs-2.js import Component from '@ember/component'; +import { inject } from '@ember/service'; +import { get } from '@ember/object'; export default Component.extend({ + flashMessages: inject(), actions: { - onChange(name) { - alert(`Custom action invoked. '${name}' tab selected!`) + onChange(changedTo) { // changedTo, ChangedFrom, event params accepted + const flashMessages = get(this, 'flashMessages'); + flashMessages.info(`Custom action invoked. '${changedTo}' tab selected!`, { + timeout: 1500 + }); } } }); diff --git a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js new file mode 100644 index 00000000..a36cb3de --- /dev/null +++ b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js @@ -0,0 +1,29 @@ +// BEGIN-SNIPPET nucleus-tabs-3.js +import Component from '@ember/component'; +import { inject } from '@ember/service'; +import RSVP from 'rsvp'; +import { get } from '@ember/object'; + +export default Component.extend({ + flashMessages: inject(), + actions: { + beforeChange() { // changedTo, changedFrom, event params accepted + const flashMessages = get(this, 'flashMessages'); + flashMessages.info(`Performing asyncronous operation..`, { + timeout: 1500 + }); + return new RSVP.Promise(function(resolve) { + setTimeout(function () { + resolve(); + }, 2000); + }); + }, + onChange() { // changedTo, ChangedFrom, event params accepted + const flashMessages = get(this, 'flashMessages'); + flashMessages.info(`Tab changed.`, { + timeout: 1500 + }); + } + } +}); +// END-SNIPPET \ No newline at end of file diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs index 65c9a732..37000e9c 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs @@ -17,5 +17,5 @@ {{/demo.example}} {{demo.snippet "nucleus-tabs-2.hbs"}} - {{demo.snippet "modal-component.js" label="component.js"}} + {{demo.snippet "nucleus-tabs-2.js" label="component.js"}} {{/docs-demo}} \ No newline at end of file diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs new file mode 100644 index 00000000..f2de6f4c --- /dev/null +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs @@ -0,0 +1,22 @@ +{{#docs-demo as |demo|}} + {{#demo.example name="nucleus-tabs-3.hbs" }} + {{#nucleus-tabs + description="site-navigation" + selected="I want apples" + beforeChange=(action "beforeChange") + onChange=(action "onChange") as |tabs| }} + {{#tabs.panel props=tabs.props name="I want apples" }} +
This is apples section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want oranges" }} +
This is oranges section
+ {{/tabs.panel}} + {{#tabs.panel props=tabs.props name="I want grapes" }} +
This is grapes section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + {{/demo.example}} + + {{demo.snippet "nucleus-tabs-3.hbs"}} + {{demo.snippet "nucleus-tabs-3.js" label="component.js"}} +{{/docs-demo}} \ No newline at end of file diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index 6c7ef183..53373006 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -32,14 +32,18 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{demo.snippet 'nucleus-tabs.hbs'}} {{/docs-demo}} -#### 2. Custom actions on changing tab +#### 2. Custom action 'on' changing tab {{nucleus-tabs/demo-1}} -#### 3. Disabled tab +#### 3. Custom action 'before' changing tab + +{{nucleus-tabs/demo-2}} + +#### 4. Disabled tab {{#docs-demo as |demo|}} - {{#demo.example name="nucleus-tabs-3.hbs"}} + {{#demo.example name="nucleus-tabs-4.hbs"}} {{#nucleus-tabs description="site-navigation" selected="I want apples" @@ -55,7 +59,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{/tabs.panel}} {{/nucleus-tabs}} {{/demo.example}} - {{demo.snippet 'nucleus-tabs-3.hbs'}} + {{demo.snippet 'nucleus-tabs-4.hbs'}} {{/docs-demo}} ## Styles diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 0c597e56..b76d83de 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -132,10 +132,17 @@ class NucleusTabs extends Component { * */ @action - activateTab(name, event) { - set(this, 'currentSelected', name); - if(this.onChange) { - this.onChange(name, event); + async activateTab(changedTo, event) { + let _this = this; + const beforeChange = get(_this, 'beforeChange'); + const onChange = get(_this, 'onChange'); + const currentTab = get(_this, 'currentSelected'); + if(beforeChange) { + await beforeChange.call(_this, changedTo, currentTab, event); + } + set(_this, 'currentSelected', changedTo); + if(onChange) { + await onChange.call(_this, changedTo, currentTab, event); } } } diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_background_style_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_background_style_tabs__assert0_0_document_0_webview.png index eeb77a3a63b4d6adedd30dfe928e3a25f5c79e38..578e1a07d2d577156e0116da201ca640b4a1100e 100644 GIT binary patch literal 11331 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 literal 11339 zcmeHNX;@R&y2etaR*`x=)}jox#-Rt1N*H7au~mza$<|s5VbC%}3=kzi2q6j93dgEY zgM!Rj6>5cm1cD49Dn}531VIP^l7L_W0s#_|5E7EJcIdBrpYxpFfA_f$`LkAb_Fij! z?|R?&`}W?seB9U5^uw(m8W|axqK+Q^*2rjemywb2&mXJ-NAA48=@@8M;lK6#%81Uf zm@qQ>)ChI>%kPp3rUazwz6gP2jvILqQ|Zlra4X`{PWkvf{!iZ&d|Woaza;!p?t^=; zkHtlKcEA4idTVnx#)lqxBW;71cW0Hw>)%V#?ycJPk-Y!sbkF=CS}gM;%=^knz1ZwW z6ZPABJ4y43MUxqgZi!9~<|!*wy7I}`sJxQ3U@=BUmAM{nr1uQnu5D|_4V~j3b*+Lr zw?DmY47CTpJZJ*7sXwJ|fLf!~t1J!ehvZtxZwi4RfWd@gc?tI$qI9qDhQU$`WAj&$ z$pk`ndCGoU)S_}a%fr^T;#sxfx>J&Y$b+HaBJhhMhP=7NFpUwb*@=5Fw1I9@UY>Ex zFypWyB0r+BA5EHYBAS^c0~fn6Lp?^@i{{_N>*QJL zF={eCw%7r7NXYc$sCl~?OokiIy4uqHs+;&&|+zn*-OLe+flVjReVI7z^I72***|!CnJ~gDwfAzc{c|6r(F&h!r zF+#$S3ElkOF`>w|{R);RDc-=hv9)b8Ntfm;1Z@T2=m<^(FDKI;>9u0$8rhvN5yQgEG%2^O<&T%%_}ijEAl{2wt)}FCr^4lj;@x(mZNA} zcy#Vvzw%==2cPb4n#CLkkp+`mp|7i~)h*Rb=KN5thF+e3jWM~8N?scJjkCU=;kXH@ zW>V+p4Lh7d+^CT52EHm!%Wu!+_)(35>?u<&WopL zbdKr!jXfE(eChPKBSiKp3OOshxkrrZ?(H30(o<$&2AE(IKh*D=bZx*-jo|vvi57Q( zzTUniZCiuRp3}nf$y%==7}H>(O*%8;?B$zkq5g+k89S-hfI)yy<%7P9Kj>UvnViE`Ft{Nn9?9_DMl27epUDpe;si=UzWyNjFeR)-|CqaZLf=& zl840y4uEaPl<3M3DkNZhPFcI#h9QI~SC>*06`BP_rPEB&J|`e^kajaK^J*sH?~qgZ z+1X=MAsdFwXB^-rmbKl-upAqt-B?|kVPyyHy1N~=6|3mBf%Wzc(^c}!W1&2L_y{e* zyjnz$isCC3=qlNBML^YWlbbUQ5`Tgn)I0xa9w9y)yJcN)2Z}@vZDk6%Lg8DWz=Bkf5I2gu>v8hS8wDvdIDd3BxI;cAtsL z;{|M7cFXC?PWZBs4l9oSI|u!%q2A%GnWxj~-o+jWGsJw?#}&D5gxJWATXR({?9mK2 zd#X;GKZ8@Q{1U)QnBI*gqc5Wt^!|?e2~5P?qLs* z${{m^gQsP}cYy#f@bzwn>)&HyVxpp=+HQP)B2Lz}TXM*H@wl{MrpY$LF{=CCRcj
niw)_s8~B1RTb zb?QRIW(cR@1nEh$@%9`$t{r9Wiu>F@HxY*~m~^wL`hui4m!W>uxshin@_dmu7K<(M zEv)Z+6w6*x4pH?>Ztm%nrN#R!79v!?$RjKWZ`$~ae_FTiC~du^t3%cMs?kCPIYF4?7QU-$9e7Z*&d9&f^|hE`Fz>Agi0o)po$S9oIdr za)Swig#57S8eRwhjvUhGJ}@v)H5l74-GvYc1THg;awpP2if3zo-gJN*+eX{zWgk2A zI6g5^*~qKKh0a+(V~=kRQKWjLEX7|(`d`J_VcZlJp2qZ2L1?|!#9|IdwYUB>QuW^#O-1vlJg8sV*$gdhq4aJ~aS(S6_L z?Tr)mr=FyyOn8M3Of^OIl)UW^%k)8`(O+j+xTc9ZFl@ zmcR?n32%Pn08lcskM6Hb8Qms&!Pd63!=I$CGIxspp(y#?gN|GKcYu>oU^I$$)LM15 zHoT2ikkA)^C=SMVI-<3L=oo4(v&9@98Qx!c+T1?;s4sG9-hFZWE^*}2mbitHbc{}` zu9LmA!4I8pjez=NG%skqoqZ`YER{ISTJysOKza+()Fbsl^nYfn)QK2K>*$%jTM^4lH{h;&DHwV<5J^8U+ zNI@N5BL3qc{iTOyDyuDPHwFq89_by+QuGCprfXalhA+z7{3ulzoKuEMBhq_p36B3} zU*qgEt(3sFk<{E@5)5p`aEl7fQihG|6n$tppkjE-RdaJBB}F-iW4r4XMp3j1isE4| zfaLq~z?&kyP8IOz$0KX;d@BnMbWwUY?$w z;2?8;deAv1ltNSvU@I*1B(+V7@~c(|*HE1-Cky81p$?~wYeI=1e}A{4I+fSaOdrNxwFHwxB(GrAAp zT(X@61j7OR>X#?y1Sd|YXMkHqO!Ys?btlFNm!t#?r@+NUJ(g8FiD^`^W;$j%jP^7Q{l zI0mEzf0hva%<}W@8|GynuWY%6yE-&H*uT`X<|S)Y8&2;PJVUNWWl0Vq#Oc zExKwVf$O1(c6D{F0`AZ_cMa!FYgBjQe5*#<7TJQ#mnFPtcZ}8tmwY1Qz?***sd)5Y zw6Fn4uzRbsRkN(N^+8!+&DkrhW6`c`4u=DTGd7ZLEu6|T-f-Yku-78G!cf*W2C;N& z=cjuCop4mmVBPGqb@GMLOw8=?)4Dgm*f1xB=~l^m{M_dbgWQ-Gs(rHsMFZx5n_Ikw zDaBy-sPN{ZHmaTMQ3~2u^dow+PPK?5FNs^ zP+QJY3Ej+)+H+Rq`B#8IcQKT0nb8yP-W`+iN*llqO~Kk`N1EXY?_LL`%snSc>m*KY z+2JH!OshgDofhCZZ9oSMkYbeXmnixrl>}&j^V8~e z$>RF`kNs|au^knsnXcWj6zEMEpQhsUs@y=|vM~Bp4$fm9-0e)h%h9okb~le2yUY7l*LrJ@Ms+W6cv|rws9;j0_+l*0!PsO&G_S zY}3MXJE!J!Gn@{1-%WWDa13sZT-=Gxa1r0yg~GqNyxWO^;l^?fj9vv_`%kAk;>8_N zee~eL0!+tce8f)CsCEJ}k=Db>bcrd@da03qLpfq-c`fD0>+vKo5$_ z%g=*NLXc?ppv2FJpXe?+?+0oeFqM!faa0LOIK%2tc|E>vNsi{Nw@x^U$AJ!$zaSNF-7q8qE+*3dehD3g3vFF(jwAQ3xLF7JVk8fg*#>nApU^?u{uKJr-^!05vj3|24z@>}7- z3I>o3ukc_6gB2e9`ATRdH?H`>iYG#Lu)>2C4E_oa%5B%w85ylpL;nT%3-8?jUm0ww zo|j?y0ff>Avf7YSKR+|{)Bb#0wte~a*{KhfZ+$;`Z#l=T{A_q$v;xZt9e{9BS5#tf z*%fzK{@cL{9aiW75Lii)D@hX2V8s>x&%5HrfA08bv?QW#81gOD5#Ph~uTK5+FLRD) AC;$Ke diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_disabled_tabs__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_disabled_tabs__assert0_0_document_0_webview.png index 65fa22562a47c162f1579c697eea251c93f7086a..5a7f6d94f43b87f19b053835fdff413a33e00a84 100644 GIT binary patch delta 4933 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 WjsA>eJx6Zwk2-$xOU@T(fBrvm)`-La delta 4941 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 diff --git a/packages/tabs/tests/integration/components/nucleus-tabs-test.js b/packages/tabs/tests/integration/components/nucleus-tabs-test.js index 09da2b9f..48c1bc83 100644 --- a/packages/tabs/tests/integration/components/nucleus-tabs-test.js +++ b/packages/tabs/tests/integration/components/nucleus-tabs-test.js @@ -93,9 +93,9 @@ module('Integration | Component | nucleus-tabs', function(hooks) { assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').hasText('home'); }); - test('it should yeilds onchange action', async function(assert) { - let onchangeAction = this.spy(); - this.actions.onChange = onchangeAction; + test('it should yeilds onChange action', async function(assert) { + let onChangeAction = this.spy(); + this.actions.onChange = onChangeAction; await render(hbs` {{#nucleus-tabs description="site-navigation" selected="home" onChange=(action "onChange") as |tabs|}} {{#tabs.panel name="home" props=tabs.props }} @@ -108,7 +108,51 @@ module('Integration | Component | nucleus-tabs', function(hooks) { `); await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); - assert.ok(onchangeAction.calledOnce, 'onChange action has been called.'); + assert.ok(onChangeAction.calledOnce, 'onChange action has been called.'); + }); + + test('it should yeilds beforeChange action', async function(assert) { + let beforeChangeAction = this.spy(); + this.actions.beforeChange = beforeChangeAction; + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" beforeChange=(action "beforeChange") as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props }} +
This is the about section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + + await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); + }); + + test('it should call beforeChange action ahead of onChange action', async function(assert) { + let beforeChangeAction = this.spy(function() { + assert.step('beforeChange'); + }); + this.actions.beforeChange = beforeChangeAction; + let onChangeAction = this.spy(function() { + assert.step('onChange'); + }); + this.actions.onChange = onChangeAction; + await render(hbs` + {{#nucleus-tabs description="site-navigation" selected="home" beforeChange=(action "beforeChange") onChange=(action "onChange") as |tabs|}} + {{#tabs.panel name="home" props=tabs.props }} +
This is the home section
+ {{/tabs.panel}} + {{#tabs.panel name="about" props=tabs.props }} +
This is the about section
+ {{/tabs.panel}} + {{/nucleus-tabs}} + `); + + await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); + assert.ok(onChangeAction.calledOnce, 'beforeChange action has been called.'); + assert.verifySteps(['beforeChange', 'onChange']); }); test('it has accessibility attributes', async function(assert) { From 0d3c03d014cea71049eff70a9e266c6f8a8f0376 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Fri, 6 Mar 2020 15:56:31 +0530 Subject: [PATCH 09/21] feat(tabs): Adding customClasses prop to tabs --- .../app/templates/docs/components/nucleus-tabs.md | 1 + packages/tabs/addon/components/nucleus-tabs.js | 12 ++++++++++++ .../templates/components/nucleus-tabs/tab-panel.hbs | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index 53373006..e79a985d 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -15,6 +15,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#docs-demo as |demo|}} {{#demo.example name="nucleus-tabs.hbs"}} {{#nucleus-tabs + customClasses="contacts-tab contacts-tab-simple" description="site-navigation" selected="I want apples" variant="default" as |tabs| }} diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index b76d83de..d961e8c1 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -19,6 +19,7 @@ import layout from "../templates/components/nucleus-tabs"; @templateLayout(layout) @classNames('nucleus-tabs') @classNameBindings('variantClass') +@classNameBindings('customClasses') class NucleusTabs extends Component { /** * Description : to add aria label @@ -32,6 +33,17 @@ class NucleusTabs extends Component { @defaultProp description = null; + /** + * customClasses : to add custom class to the tabs component + * + * @field customClasses + * @type string + * @readonly + * @public + */ + @defaultProp + customClasses = ""; + /** * selected : default open tab * diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs index fb5c4b15..889d9eea 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs/tab-panel.hbs @@ -1 +1 @@ -{{yield}} \ No newline at end of file +{{yield}} From 8167111d25e5b2533ace89babb68266ab48d6f22 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Fri, 6 Mar 2020 17:05:02 +0530 Subject: [PATCH 10/21] refractor(tabs): class names changed to follow existing conventions --- packages/tabs/CHANGELOG.md | 4 ++ .../tabs/addon/components/nucleus-tabs.js | 14 +++---- .../components/nucleus-tabs/tab-list-item.js | 39 ++++++++++++------- .../components/nucleus-tabs/tab-panel.js | 19 ++++----- .../styles/components/_nucleus-tabs.scss | 16 ++++---- .../templates/components/nucleus-tabs.hbs | 4 +- .../components/nucleus-tabs/tab-list-item.hbs | 2 +- 7 files changed, 57 insertions(+), 41 deletions(-) diff --git a/packages/tabs/CHANGELOG.md b/packages/tabs/CHANGELOG.md index e4d87c4d..71b67641 100644 --- a/packages/tabs/CHANGELOG.md +++ b/packages/tabs/CHANGELOG.md @@ -2,3 +2,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.0] - 2019-03-06 +### Added +- {{nucleus-tabs}} Initial imlpementation. \ No newline at end of file diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index d961e8c1..183ce972 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -1,10 +1,10 @@ -import { classNames, classNameBindings, layout as templateLayout } from '@ember-decorators/component'; -import { A } from '@ember/array'; -import defaultProp from '@freshworks/core/utils/default-decorator'; import Component from '@ember/component'; +import { classNames, classNameBindings, layout as templateLayout } from '@ember-decorators/component'; +import layout from "../templates/components/nucleus-tabs"; import { set, get, computed, action } from '@ember/object'; +import defaultProp from '@freshworks/core/utils/default-decorator'; +import { A } from '@ember/array'; import { oneWay }from '@ember/object/computed'; -import layout from "../templates/components/nucleus-tabs"; /** __Usage:__ @@ -22,7 +22,7 @@ import layout from "../templates/components/nucleus-tabs"; @classNameBindings('customClasses') class NucleusTabs extends Component { /** - * Description : to add aria label + * description : to add aria label * * @field description * @type string|null @@ -145,11 +145,11 @@ class NucleusTabs extends Component { */ @action async activateTab(changedTo, event) { - let _this = this; + const _this = this; const beforeChange = get(_this, 'beforeChange'); const onChange = get(_this, 'onChange'); const currentTab = get(_this, 'currentSelected'); - if(beforeChange) { + if(beforeChange) { await beforeChange.call(_this, changedTo, currentTab, event); } set(_this, 'currentSelected', changedTo); diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index d0f0f1d1..3ce3e8e5 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -1,14 +1,14 @@ -import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import Component from '@ember/component'; +import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; +import layout from '../../templates/components/nucleus-tabs/tab-list-item'; import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { once } from '@ember/runloop'; -import layout from '../../templates/components/nucleus-tabs/tab-list-item'; import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @tagName('button') @templateLayout(layout) -@classNames('nucleus-tabs--list--item') +@classNames('nucleus-tabs__list__item') @classNameBindings('isActive:active') @classNameBindings('isDisabled:disabled') @attributeBindings('tabindex') @@ -23,7 +23,7 @@ class TabListItem extends Component { * * @field disabled * @type string - * @default 'false' + * @default null * @readonly * @public */ @@ -35,7 +35,6 @@ class TabListItem extends Component { * * @field controls * @type string|null - * @default null * @readonly * @public */ @@ -150,9 +149,9 @@ class TabListItem extends Component { */ keyDown(event) { event.stopPropagation(); - let target = event.target; - let firstElement = target.parentElement.firstElementChild; - let lastElement = target.parentElement.lastElementChild; + const target = event.target; + const firstElement = target.parentElement.firstElementChild; + const lastElement = target.parentElement.lastElementChild; const keyCode = TABS_KEY_CODE; switch (event.keyCode) { @@ -176,10 +175,16 @@ class TabListItem extends Component { } } + /** + * focusNextTab : focus the next tab that is not disabled. + * When last item, focus must circle back to previous item. + * + * @method focusNextTab + * @public + * + */ focusNextTab(element, elementInFocus) { - let nextElement = element.nextElementSibling; - nextElement = (nextElement)? nextElement : element.parentElement.firstElementChild; - + const nextElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; if(elementInFocus && (elementInFocus.id === nextElement.id)) { return; } else if(nextElement.disabled) { @@ -189,10 +194,16 @@ class TabListItem extends Component { } } + /** + * focusPreviousTab : focus the previous tab that is not disabled. + * When first item, focus must go back to last item. + * + * @method focusPreviousTab + * @public + * + */ focusPreviousTab(element, elementInFocus) { - let previousElement = element.previousElementSibling; - previousElement = (previousElement)? previousElement : element.parentElement.lastElementChild; - + const previousElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; if(elementInFocus && (elementInFocus.id === previousElement.id)) { return; } else if(previousElement.disabled) { diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 2adf2529..7a561a5f 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -1,13 +1,13 @@ -import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import Component from '@ember/component'; -import { get, computed } from '@ember/object'; +import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import layout from '../../templates/components/nucleus-tabs/tab-panel'; -import { once } from '@ember/runloop'; +import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; +import { once } from '@ember/runloop'; @tagName('div') @templateLayout(layout) -@classNames('nucleus-tabs--panel') +@classNames('nucleus-tabs__panel') @classNameBindings('isActive:active') @attributeBindings('tabindex') @attributeBindings('role') @@ -30,6 +30,7 @@ class TabPanel extends Component { * tabindex * * @field tabindex + * @default '0' * @type String * @public */ @@ -57,15 +58,15 @@ class TabPanel extends Component { isActive; /** - * isActive + * aria-labelledby * - * @field isActive - * @type Boolean + * @field aria-labelledby + * @type String * @public */ @computed('props.tabList.[]', function() { - let tabListItems = get(this.props, 'tabListItems'); - let tabList = tabListItems.findBy('name', get(this, 'name')); + const tabListItems = get(this.props, 'tabListItems'); + const tabList = tabListItems.findBy('name', get(this, 'name')); return (tabList)? tabList.id : ""; }) "aria-labelledby"; diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 8dedad32..c1d8f37e 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -2,11 +2,11 @@ display: flex; flex-direction: column; - &--list { + &__list { width: 100%; overflow: scroll hidden; - &--container { + &__container { display: flex; flex-wrap: nowrap; width: 100%; @@ -14,7 +14,7 @@ border-bottom: 1px solid $color-smoke-50; } - &--item { + &__item { all: unset; flex-shrink: 0; position: relative; @@ -58,7 +58,7 @@ } } - &--item.disabled { + &__item.disabled { color: $color-smoke-300; -webkit-text-fill-color: $color-smoke-300; @@ -71,7 +71,7 @@ } } - &--item.active { + &__item.active { color: $color-azure-800; &::after { @@ -80,18 +80,18 @@ } } - &--panel { + &__panel { width: 100%; display: none; padding: 8px 20px; } - &--panel.active { + &__panel.active { display: block; } &--background { - .nucleus-tabs--list { + .nucleus-tabs__list { background: $color-smoke-25; border: 1px solid $color-smoke-50; border-radius: 4px; diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index ac1ca745..442734c6 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -1,5 +1,5 @@ -
-
+
+
{{#each tabPanels key="id" as |tab index|}} {{#nucleus-tabs/tab-list-item name=tab.name diff --git a/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs b/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs index fb5c4b15..889d9eea 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs/tab-list-item.hbs @@ -1 +1 @@ -{{yield}} \ No newline at end of file +{{yield}} From a177a2d71c730aeffda2f49b4a380f41eb6d346c Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Fri, 6 Mar 2020 17:41:18 +0530 Subject: [PATCH 11/21] refractor(tabs): Added missing empty lines at the end of files --- .../app/components/nucleus-tabs/demo-1.js | 2 +- .../app/components/nucleus-tabs/demo-2.js | 2 +- .../components/nucleus-tabs/demo-1.hbs | 2 +- .../components/nucleus-tabs/demo-2.hbs | 2 +- packages/tabs/README.md | 2 - .../tabs/addon/components/nucleus-tabs.js | 2 +- .../components/nucleus-tabs/tab-list-item.js | 37 ++++++++++++---- .../components/nucleus-tabs/tab-panel.js | 10 +++++ packages/tabs/addon/constants/nucleus-tabs.js | 2 +- .../templates/components/nucleus-tabs.hbs | 2 +- packages/tabs/addon/utils/safe-set.js | 34 --------------- packages/tabs/app/components/nucleus-tabs.js | 2 +- .../components/nucleus-tabs/tab-list-item.js | 2 +- .../app/components/nucleus-tabs/tab-panel.js | 2 +- .../components/nucleus-tabs-test.js | 42 +++++++++---------- 15 files changed, 69 insertions(+), 76 deletions(-) delete mode 100644 packages/tabs/addon/utils/safe-set.js diff --git a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js index 856eecc3..54b22ed0 100644 --- a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js +++ b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-1.js @@ -14,4 +14,4 @@ export default Component.extend({ } } }); -// END-SNIPPET \ No newline at end of file +// END-SNIPPET diff --git a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js index a36cb3de..ea26f50c 100644 --- a/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js +++ b/packages/@nucleus/tests/dummy/app/components/nucleus-tabs/demo-2.js @@ -26,4 +26,4 @@ export default Component.extend({ } } }); -// END-SNIPPET \ No newline at end of file +// END-SNIPPET diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs index 37000e9c..67f7876f 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs @@ -18,4 +18,4 @@ {{demo.snippet "nucleus-tabs-2.hbs"}} {{demo.snippet "nucleus-tabs-2.js" label="component.js"}} -{{/docs-demo}} \ No newline at end of file +{{/docs-demo}} diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs index f2de6f4c..c0d424b9 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs @@ -19,4 +19,4 @@ {{demo.snippet "nucleus-tabs-3.hbs"}} {{demo.snippet "nucleus-tabs-3.js" label="component.js"}} -{{/docs-demo}} \ No newline at end of file +{{/docs-demo}} diff --git a/packages/tabs/README.md b/packages/tabs/README.md index 2323143b..19854beb 100644 --- a/packages/tabs/README.md +++ b/packages/tabs/README.md @@ -20,5 +20,3 @@ Guidelines **Dont's** 1. Dont use tabs for sequential content. Users can navigate to any tab at any time and cannot be expected to do it sequentially. - - diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 183ce972..511eb2dd 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -11,7 +11,7 @@ import { oneWay }from '@ember/object/computed'; [Refer component page](/docs/components/nucleus-tabs) - @class Nucleus Tab + @class Nucleus Tabs @namespace Components @extends Ember.Component @public diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 3ce3e8e5..70dca8e4 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -51,6 +51,25 @@ class TabListItem extends Component { */ role = 'tab'; + /** + * tabOrder : order in which tabs are displayed. Only first tabs will not have tabIndex value. + * + * @field tabOrder + * @type number + * @readonly + * @public + */ + tabOrder; + + /** + * currentSelected : currently selected tab + * + * @field currentSelected + * @readonly + * @public + */ + currentSelected; + /** * tabindex * @@ -58,8 +77,8 @@ class TabListItem extends Component { * @type string|null * @public */ - @computed('index', function() { - return (get(this, 'index') === 0)? null : '-1'; + @computed('tabOrder', function() { + return (get(this, 'tabOrder') === 0)? null : '-1'; }) tabindex; @@ -136,7 +155,7 @@ class TabListItem extends Component { click(event) { event.target.focus(); if(get(this, 'disabled') === 'false') { - this.handleActivateTab(get(this, 'name'), event); + get(this, 'handleActivateTab').call(this, get(this, 'name'), event); } } @@ -149,14 +168,14 @@ class TabListItem extends Component { */ keyDown(event) { event.stopPropagation(); - const target = event.target; - const firstElement = target.parentElement.firstElementChild; - const lastElement = target.parentElement.lastElementChild; + const targetElement = event.target; + const firstElement = targetElement.parentElement.firstElementChild; + const lastElement = targetElement.parentElement.lastElementChild; const keyCode = TABS_KEY_CODE; switch (event.keyCode) { case (keyCode.ENTER || keyCode.SPACE): - target.click(); + targetElement.click(); break; case keyCode.END: lastElement.focus(); @@ -165,10 +184,10 @@ class TabListItem extends Component { firstElement.focus(); break; case keyCode.LEFT: - this.focusPreviousTab(target); + get(this, 'focusPreviousTab').call(this, targetElement); break; case keyCode.RIGHT: - this.focusNextTab(target); + get(this, 'focusNextTab').call(this, targetElement); break; default: break; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 7a561a5f..cfc99b24 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -5,6 +5,16 @@ import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { once } from '@ember/runloop'; +/** + __Usage:__ + + [Refer component page](/docs/components/nucleus-tabs) + + @class Nucleus Tab + @namespace Components + @extends Ember.Component + @public +*/ @tagName('div') @templateLayout(layout) @classNames('nucleus-tabs__panel') diff --git a/packages/tabs/addon/constants/nucleus-tabs.js b/packages/tabs/addon/constants/nucleus-tabs.js index 7ada4067..8f8692a1 100644 --- a/packages/tabs/addon/constants/nucleus-tabs.js +++ b/packages/tabs/addon/constants/nucleus-tabs.js @@ -7,4 +7,4 @@ const TABS_KEY_CODE = { RIGHT: 39 }; -export { TABS_KEY_CODE }; \ No newline at end of file +export { TABS_KEY_CODE }; diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index 442734c6..b705dca9 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -5,7 +5,7 @@ name=tab.name disabled=tab.disabled controls=tab.id - index=index + tabOrder=index currentSelected=currentSelected registerTabListItem=(action registerTabListItem) handleActivateTab=(action activateTab)}} diff --git a/packages/tabs/addon/utils/safe-set.js b/packages/tabs/addon/utils/safe-set.js deleted file mode 100644 index 79c2b9fe..00000000 --- a/packages/tabs/addon/utils/safe-set.js +++ /dev/null @@ -1,34 +0,0 @@ -// [TODO] Move to core if required - -import { set } from '@ember/object'; - -/** - Use this util to safely set a property in callbacks or later runs. - - ```js - import Component from '@ember/component'; - import safeSet from "../utils/safe-set"; - import { later } from "f@ember/runloop"; - export default Component.extend({ - state: null, - action: { - setFocusState() { - later(() => { - safeSet(this, 'state', 'focussed'); - }, 1000); - } - } - }); - ``` - @function safeSet - @param {object} context reference of the instance - @param {string} key name of the property that needs to be modified - @param {string} value value to be updated -*/ - - -export default function safeSet(context, key, value) { - if (!context.isDestroyed && !context.isDestroying) { - set(context, key, value); - } -} diff --git a/packages/tabs/app/components/nucleus-tabs.js b/packages/tabs/app/components/nucleus-tabs.js index e1f8ab61..f51454bb 100644 --- a/packages/tabs/app/components/nucleus-tabs.js +++ b/packages/tabs/app/components/nucleus-tabs.js @@ -1 +1 @@ -export { default } from '@freshworks/tabs/components/nucleus-tabs'; \ No newline at end of file +export { default } from '@freshworks/tabs/components/nucleus-tabs'; diff --git a/packages/tabs/app/components/nucleus-tabs/tab-list-item.js b/packages/tabs/app/components/nucleus-tabs/tab-list-item.js index da747983..87c6a9c1 100644 --- a/packages/tabs/app/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/app/components/nucleus-tabs/tab-list-item.js @@ -1 +1 @@ -export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-list-item'; \ No newline at end of file +export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-list-item'; diff --git a/packages/tabs/app/components/nucleus-tabs/tab-panel.js b/packages/tabs/app/components/nucleus-tabs/tab-panel.js index bd5a8ea8..8279bd3a 100644 --- a/packages/tabs/app/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/app/components/nucleus-tabs/tab-panel.js @@ -1 +1 @@ -export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-panel'; \ No newline at end of file +export { default } from '@freshworks/tabs/components/nucleus-tabs/tab-panel'; diff --git a/packages/tabs/tests/integration/components/nucleus-tabs-test.js b/packages/tabs/tests/integration/components/nucleus-tabs-test.js index 48c1bc83..b651a212 100644 --- a/packages/tabs/tests/integration/components/nucleus-tabs-test.js +++ b/packages/tabs/tests/integration/components/nucleus-tabs-test.js @@ -31,21 +31,21 @@ module('Integration | Component | nucleus-tabs', function(hooks) { test('it should yield tab-list items and tab-panels', async function(assert) { await render(sampleTabsTemplate); assert.dom('.nucleus-tabs').exists({ count: 1 }, 'Tabs component exists.'); - assert.dom('.nucleus-tabs .nucleus-tabs--list').exists({ count: 1 }, 'Tabs component has a Tab list'); - assert.dom('.nucleus-tabs .nucleus-tabs--list--item').exists({ count: 3 }, 'Tabs component has 3 Tab list items'); - assert.dom('.nucleus-tabs .nucleus-tabs--panel').exists({ count: 3 }, 'Tabs component has right number of Tab panels'); + assert.dom('.nucleus-tabs .nucleus-tabs__list').exists({ count: 1 }, 'Tabs component has a Tab list'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item').exists({ count: 3 }, 'Tabs component has 3 Tab list items'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel').exists({ count: 3 }, 'Tabs component has right number of Tab panels'); }); test('it should have only selected panel as active', async function(assert) { await render(sampleTabsTemplate); - assert.dom('.nucleus-tabs .nucleus-tabs--panel.active').exists({ count: 1 }, 'Only one active panel at a time'); - assert.dom('.nucleus-tabs .nucleus-tabs--panel.active').hasText('This is the home section'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel.active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel.active').hasText('This is the home section'); }); test('it should have only selected tab list item as active', async function(assert) { await render(sampleTabsTemplate); - assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').exists({ count: 1 }, 'Only one active panel at a time'); - assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').hasText('home'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').hasText('home'); }); test('it should attach appropriate background class for the background variant', async function(assert) { @@ -75,7 +75,7 @@ module('Integration | Component | nucleus-tabs', function(hooks) { {{/tabs.panel}} {{/nucleus-tabs}} `); - assert.dom('.nucleus-tabs .nucleus-tabs--list--item.disabled').exists({ count: 1 }, 'Has disabled class when disabled prop is passed'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.disabled').exists({ count: 1 }, 'Has disabled class when disabled prop is passed'); }); test('it should not enable tab when tab list item is disabled', async function(assert) { @@ -89,8 +89,8 @@ module('Integration | Component | nucleus-tabs', function(hooks) { {{/tabs.panel}} {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); - assert.dom('.nucleus-tabs .nucleus-tabs--list--item.active').hasText('home'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').hasText('home'); }); test('it should yeilds onChange action', async function(assert) { @@ -107,7 +107,7 @@ module('Integration | Component | nucleus-tabs', function(hooks) { {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); assert.ok(onChangeAction.calledOnce, 'onChange action has been called.'); }); @@ -125,7 +125,7 @@ module('Integration | Component | nucleus-tabs', function(hooks) { {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); }); @@ -149,7 +149,7 @@ module('Integration | Component | nucleus-tabs', function(hooks) { {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs--list--item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); assert.ok(onChangeAction.calledOnce, 'beforeChange action has been called.'); assert.verifySteps(['beforeChange', 'onChange']); @@ -157,14 +157,14 @@ module('Integration | Component | nucleus-tabs', function(hooks) { test('it has accessibility attributes', async function(assert) { await render(sampleTabsTemplate); - assert.dom('.nucleus-tabs .nucleus-tabs--list').hasAttribute('role', 'tablist'); - assert.dom('.nucleus-tabs .nucleus-tabs--list').hasAttribute('aria-label', 'site-navigation'); - assert.dom('.nucleus-tabs .nucleus-tabs--list button').hasAttribute('role', 'tab'); - assert.dom('.nucleus-tabs .nucleus-tabs--list button.active').hasAttribute('aria-selected', 'true'); - assert.dom('.nucleus-tabs .nucleus-tabs--list button:not(.active)').hasAttribute('aria-selected', 'false'); - assert.dom('.nucleus-tabs .nucleus-tabs--list button').hasAttribute('aria-controls'); - assert.dom('.nucleus-tabs .nucleus-tabs--panel').hasAttribute('role', 'tabpanel'); - assert.dom('.nucleus-tabs .nucleus-tabs--panel').hasAttribute('aria-labelledby'); + assert.dom('.nucleus-tabs .nucleus-tabs__list').hasAttribute('role', 'tablist'); + assert.dom('.nucleus-tabs .nucleus-tabs__list').hasAttribute('aria-label', 'site-navigation'); + assert.dom('.nucleus-tabs .nucleus-tabs__list button').hasAttribute('role', 'tab'); + assert.dom('.nucleus-tabs .nucleus-tabs__list button.active').hasAttribute('aria-selected', 'true'); + assert.dom('.nucleus-tabs .nucleus-tabs__list button:not(.active)').hasAttribute('aria-selected', 'false'); + assert.dom('.nucleus-tabs .nucleus-tabs__list button').hasAttribute('aria-controls'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel').hasAttribute('role', 'tabpanel'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel').hasAttribute('aria-labelledby'); }); test('it passes a11y tests', async function(assert) { From d4b5ade3a52bde0f8d75d79783abe661ccb4ab88 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Mon, 9 Mar 2020 14:42:41 +0530 Subject: [PATCH 12/21] fix(tabs): documentation issues --- .../tabs/addon/components/nucleus-tabs.js | 30 +++++++---- .../components/nucleus-tabs/tab-list-item.js | 50 +++++++++++++------ .../components/nucleus-tabs/tab-panel.js | 10 ++-- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 511eb2dd..6b4bbc21 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -22,9 +22,10 @@ import { oneWay }from '@ember/object/computed'; @classNameBindings('customClasses') class NucleusTabs extends Component { /** - * description : to add aria label + * description * * @field description + * @description to add aria label * @type string|null * @default null * @readonly @@ -34,9 +35,10 @@ class NucleusTabs extends Component { description = null; /** - * customClasses : to add custom class to the tabs component + * customClasses * * @field customClasses + * @description to add custom class to the tabs component * @type string * @readonly * @public @@ -45,9 +47,10 @@ class NucleusTabs extends Component { customClasses = ""; /** - * selected : default open tab + * selected * * @field selected + * @description default open tab * @type string|null * @default null * @readonly @@ -57,9 +60,10 @@ class NucleusTabs extends Component { selected = null; /** - * variant: tab styles, line/background + * variant * * @field variant + * @description tab styles, line/background * @type string * @default 'line' * @readonly @@ -69,27 +73,30 @@ class NucleusTabs extends Component { variant = 'line'; /** - * tabPanels: Collection of all tab panels + * tabPanels * * @field tabPanels - * @type Array + * @description Collection of all tab panels + * @type array * @public */ tabPanels = A([]); /** - * tabListItems: Collection of all tab list items + * tabListItems * * @field tabListItems - * @type Array + * @description Collection of all tab list items + * @type array * @public */ tabListItems = A([]); /** - * default + * currentSelected * - * @field currentSelected : takes intial value from selected + * @field currentSelected + * @description takes intial value from selected * @type string|null * @public */ @@ -135,9 +142,10 @@ class NucleusTabs extends Component { } /** - * activateTab : Handler that will be called when a tab is clicked + * activateTab * * @method activateTab + * @description Handler that will be called when a tab is clicked * @param {string} name * @param {any} event * @public diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 70dca8e4..9ac397e5 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -6,6 +6,16 @@ import defaultProp from '@freshworks/core/utils/default-decorator'; import { once } from '@ember/runloop'; import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' +/** + __Usage:__ + + [Refer component page](/docs/components/nucleus-tabs) + + @class Nucleus Tab List Item + @namespace Components + @extends Ember.Component + @private +*/ @tagName('button') @templateLayout(layout) @classNames('nucleus-tabs__list__item') @@ -31,9 +41,10 @@ class TabListItem extends Component { disabled = null; /** - * controls : idref to what panel this tab item controls + * controls * * @field controls + * @description idref to what panel this tab item controls * @type string|null * @readonly * @public @@ -52,9 +63,10 @@ class TabListItem extends Component { role = 'tab'; /** - * tabOrder : order in which tabs are displayed. Only first tabs will not have tabIndex value. + * tabOrder * * @field tabOrder + * @description order in which tabs are displayed. Only first tabs will not have tabIndex value * @type number * @readonly * @public @@ -62,9 +74,10 @@ class TabListItem extends Component { tabOrder; /** - * currentSelected : currently selected tab + * currentSelected * * @field currentSelected + * @description currently selected tab * @readonly * @public */ @@ -131,9 +144,10 @@ class TabListItem extends Component { "aria-selected"; /** - * init : lifecycle event + * init * * @method init + * @description lifecycle event * @public * */ @@ -146,9 +160,10 @@ class TabListItem extends Component { } /** - * click : event handler + * click * * @method click + * @description event handler * @public * */ @@ -160,9 +175,10 @@ class TabListItem extends Component { } /** - * keyDown : event handler + * keyDown * * @method keyDown + * @description event handler * @public * */ @@ -184,10 +200,10 @@ class TabListItem extends Component { firstElement.focus(); break; case keyCode.LEFT: - get(this, 'focusPreviousTab').call(this, targetElement); + get(this, '_focusPreviousTab').call(this, targetElement); break; case keyCode.RIGHT: - get(this, 'focusNextTab').call(this, targetElement); + get(this, '_focusNextTab').call(this, targetElement); break; default: break; @@ -195,14 +211,15 @@ class TabListItem extends Component { } /** - * focusNextTab : focus the next tab that is not disabled. + * _focusNextTab * When last item, focus must circle back to previous item. * - * @method focusNextTab - * @public + * @method _focusNextTab + * @description focus the next tab that is not disabled + * @private * */ - focusNextTab(element, elementInFocus) { + _focusNextTab(element, elementInFocus) { const nextElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; if(elementInFocus && (elementInFocus.id === nextElement.id)) { return; @@ -214,14 +231,15 @@ class TabListItem extends Component { } /** - * focusPreviousTab : focus the previous tab that is not disabled. + * _focusPreviousTab * When first item, focus must go back to last item. * - * @method focusPreviousTab - * @public + * @method _focusPreviousTab + * @description focus the previous tab that is not disabled + * @private * */ - focusPreviousTab(element, elementInFocus) { + _focusPreviousTab(element, elementInFocus) { const previousElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; if(elementInFocus && (elementInFocus.id === previousElement.id)) { return; diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index cfc99b24..4400c9f0 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -10,7 +10,7 @@ import { once } from '@ember/runloop'; [Refer component page](/docs/components/nucleus-tabs) - @class Nucleus Tab + @class Nucleus Tab Panel @namespace Components @extends Ember.Component @public @@ -41,7 +41,7 @@ class TabPanel extends Component { * * @field tabindex * @default '0' - * @type String + * @type string * @public */ tabindex = '0'; @@ -50,7 +50,7 @@ class TabPanel extends Component { * role * * @field role - * @type String + * @type string * @public */ role = 'tabpanel' @@ -59,7 +59,7 @@ class TabPanel extends Component { * isActive * * @field isActive - * @type Boolean + * @type boolean * @public */ @computed('props.[]', function() { @@ -71,7 +71,7 @@ class TabPanel extends Component { * aria-labelledby * * @field aria-labelledby - * @type String + * @type string * @public */ @computed('props.tabList.[]', function() { From a4f3033f092f6ccf42d34196961499d3af163128 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Tue, 10 Mar 2020 15:20:39 +0530 Subject: [PATCH 13/21] fix(tabs): safari font color on active element --- .../components/nucleus-tabs/tab-list-item.js | 17 ++++++++++++++-- .../styles/components/_nucleus-tabs.scss | 20 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 9ac397e5..39a01de1 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -26,6 +26,7 @@ import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @attributeBindings('aria-controls') @attributeBindings('aria-selected') @attributeBindings('isDisabled:disabled') +@attributeBindings('title') class TabListItem extends Component { /** @@ -119,6 +120,18 @@ class TabListItem extends Component { }) isDisabled; + /** + * title + * + * @field title + * @type string + * @public + */ + @computed('name', function() { + return get(this, 'name'); + }) + title; + /** * aria-controls * @@ -224,7 +237,7 @@ class TabListItem extends Component { if(elementInFocus && (elementInFocus.id === nextElement.id)) { return; } else if(nextElement.disabled) { - get(this, 'focusNextTab').call(this, nextElement, element); + get(this, '_focusNextTab').call(this, nextElement, element); } else { nextElement.focus(); } @@ -244,7 +257,7 @@ class TabListItem extends Component { if(elementInFocus && (elementInFocus.id === previousElement.id)) { return; } else if(previousElement.disabled) { - get(this, 'focusPreviousTab').call(this, previousElement, element); + get(this, '_focusPreviousTab').call(this, previousElement, element); } else { previousElement.focus(); } diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index c1d8f37e..82c552da 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -24,6 +24,24 @@ margin: 0 4px; font-size: 14px; color: $color-smoke-700; + -webkit-text-fill-color: $color-smoke-700; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + + &::before { + display: block; + content: attr(title); + font-weight: 500; + height: 0; + overflow: hidden; + visibility: hidden; + } &::after { content: ''; @@ -73,6 +91,8 @@ &__item.active { color: $color-azure-800; + -webkit-text-fill-color: $color-azure-800; + font-weight: 500; &::after { border-bottom: 2px solid $color-azure-800; From ad5af241603faea936644ea80af46e08576a3a38 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 11 Mar 2020 17:42:15 +0530 Subject: [PATCH 14/21] refractor(tabs): Removed sending attributes in single props object --- .../components/nucleus-tabs/demo-1.hbs | 8 +- .../components/nucleus-tabs/demo-2.hbs | 8 +- .../templates/docs/components/nucleus-tabs.md | 38 +++---- .../tabs/addon/components/nucleus-tabs.js | 20 ++-- .../components/nucleus-tabs/tab-list-item.js | 65 +++++++---- .../components/nucleus-tabs/tab-panel.js | 27 +++-- .../styles/components/_nucleus-tabs.scss | 41 +++---- .../templates/components/nucleus-tabs.hbs | 11 +- ...disabled__assert0_0_document_0_webview.png | Bin 0 -> 26366 bytes ...variants__assert0_0_document_0_webview.png | Bin 0 -> 26409 bytes .../components/nucleus-tabs-test.js | 104 ++++++++---------- 11 files changed, 169 insertions(+), 153 deletions(-) create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants_-_default_background_disabled__assert0_0_document_0_webview.png create mode 100644 packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants__assert0_0_document_0_webview.png diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs index 67f7876f..44a33645 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs @@ -2,15 +2,15 @@ {{#demo.example name="nucleus-tabs-2.hbs" }} {{#nucleus-tabs description="site-navigation" - selected="I want apples" + select="I want apples" onChange=(action "onChange") as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" }} + {{#tabs.panel name="I want grapes" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs index c0d424b9..c6098491 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-2.hbs @@ -2,16 +2,16 @@ {{#demo.example name="nucleus-tabs-3.hbs" }} {{#nucleus-tabs description="site-navigation" - selected="I want apples" + select="I want apples" beforeChange=(action "beforeChange") onChange=(action "onChange") as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" }} + {{#tabs.panel name="I want grapes" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index e79a985d..6f9d0cf2 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -15,17 +15,17 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#docs-demo as |demo|}} {{#demo.example name="nucleus-tabs.hbs"}} {{#nucleus-tabs - customClasses="contacts-tab contacts-tab-simple" + customClasses="sample-tab sample-tab-simple" description="site-navigation" - selected="I want apples" + select="I want apples" variant="default" as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" }} + {{#tabs.panel name="I want grapes" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -47,15 +47,15 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#demo.example name="nucleus-tabs-4.hbs"}} {{#nucleus-tabs description="site-navigation" - selected="I want apples" + select="I want apples" variant="default" as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" disabled="true" }} + {{#tabs.panel name="I want grapes" disabled="true" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -71,15 +71,15 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#demo.example name="nucleus-tabs-variant1.hbs"}} {{#nucleus-tabs description="site-navigation" - selected="I want apples" + select="I want apples" variant="default" as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" }} + {{#tabs.panel name="I want grapes" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -96,15 +96,15 @@ Pass 'variant' property as 'background'. {{#demo.example name="nucleus-tabs-variant2.hbs"}} {{#nucleus-tabs description="site-navigation" - selected="I want apples" + select="I want apples" variant="background" as |tabs| }} - {{#tabs.panel props=tabs.props name="I want apples" }} + {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want oranges" }} + {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel props=tabs.props name="I want grapes" }} + {{#tabs.panel name="I want grapes" }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -143,11 +143,11 @@ __role=tab__ Indicates the element serves as a tab control. -__aria-selected=true__ +__aria-select=true__ Indicates the tab control is activated and its associated panel is displayed. -__aria-selected=false__ +__aria-select=false__ Indicates the tab control is not active and its associated panel is NOT displayed. diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 6b4bbc21..75421534 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -47,9 +47,9 @@ class NucleusTabs extends Component { customClasses = ""; /** - * selected + * select * - * @field selected + * @field select * @description default open tab * @type string|null * @default null @@ -57,7 +57,7 @@ class NucleusTabs extends Component { * @public */ @defaultProp - selected = null; + select = null; /** * variant @@ -93,15 +93,15 @@ class NucleusTabs extends Component { tabListItems = A([]); /** - * currentSelected + * selected * - * @field currentSelected - * @description takes intial value from selected + * @field selected + * @description takes intial value from select * @type string|null * @public */ - @oneWay('selected') - currentSelected; + @oneWay('select') + selected; /** * variantClass @@ -156,11 +156,11 @@ class NucleusTabs extends Component { const _this = this; const beforeChange = get(_this, 'beforeChange'); const onChange = get(_this, 'onChange'); - const currentTab = get(_this, 'currentSelected'); + const currentTab = get(_this, 'selected'); if(beforeChange) { await beforeChange.call(_this, changedTo, currentTab, event); } - set(_this, 'currentSelected', changedTo); + set(_this, 'selected', changedTo); if(onChange) { await onChange.call(_this, changedTo, currentTab, event); } diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 39a01de1..62418fec 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -3,7 +3,6 @@ import { classNames, attributeBindings, classNameBindings, tagName, layout as te import layout from '../../templates/components/nucleus-tabs/tab-list-item'; import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; -import { once } from '@ember/runloop'; import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' /** @@ -16,11 +15,11 @@ import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @extends Ember.Component @private */ -@tagName('button') +@tagName('div') @templateLayout(layout) @classNames('nucleus-tabs__list__item') -@classNameBindings('isActive:active') -@classNameBindings('isDisabled:disabled') +@classNameBindings('isActive:is-active') +@classNameBindings('isDisabled:is-disabled') @attributeBindings('tabindex') @attributeBindings('role') @attributeBindings('aria-controls') @@ -54,14 +53,15 @@ class TabListItem extends Component { controls; /** - * role + * selected * - * @field role - * @type string - * @default 'tab' + * @field selected + * @description currently selected tab + * @readonly * @public */ - role = 'tab'; + @defaultProp + selected; /** * tabOrder @@ -72,17 +72,18 @@ class TabListItem extends Component { * @readonly * @public */ + @defaultProp tabOrder; /** - * currentSelected + * role * - * @field currentSelected - * @description currently selected tab - * @readonly + * @field role + * @type string + * @default 'tab' * @public */ - currentSelected; + role = 'tab'; /** * tabindex @@ -92,7 +93,11 @@ class TabListItem extends Component { * @public */ @computed('tabOrder', function() { - return (get(this, 'tabOrder') === 0)? null : '-1'; + let tabIndex = null; + if(!get(this, 'isDisabled')) { + tabIndex = (get(this, 'tabOrder') === 0)? "0" : "-1" + } + return tabIndex; }) tabindex; @@ -103,8 +108,8 @@ class TabListItem extends Component { * @type boolean * @public */ - @computed('currentSelected', function() { - return (get(this, 'currentSelected') === get(this, 'name')); + @computed('selected', function() { + return (get(this, 'selected') === get(this, 'name')); }) isActive; @@ -151,8 +156,8 @@ class TabListItem extends Component { * @type boolean * @public */ - @computed('currentSelected', function() { - return (get(this, 'currentSelected') === get(this, 'name')).toString(); + @computed('selected', function() { + return (get(this, 'selected') === get(this, 'name')).toString(); }) "aria-selected"; @@ -166,7 +171,18 @@ class TabListItem extends Component { */ init() { super.init(...arguments); - once(this, get(this, 'registerTabListItem'), { + } + + /** + * didInsertElement + * + * @method didInsertElement + * @description lifecycle event + * @public + * + */ + didInsertElement() { + get(this, 'registerTabListItem').call(this, { id: get(this, 'elementId'), name: get(this, 'name') }); @@ -181,7 +197,6 @@ class TabListItem extends Component { * */ click(event) { - event.target.focus(); if(get(this, 'disabled') === 'false') { get(this, 'handleActivateTab').call(this, get(this, 'name'), event); } @@ -203,7 +218,9 @@ class TabListItem extends Component { const keyCode = TABS_KEY_CODE; switch (event.keyCode) { - case (keyCode.ENTER || keyCode.SPACE): + case keyCode.ENTER: + case keyCode.SPACE: + event.preventDefault(); targetElement.click(); break; case keyCode.END: @@ -236,7 +253,7 @@ class TabListItem extends Component { const nextElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; if(elementInFocus && (elementInFocus.id === nextElement.id)) { return; - } else if(nextElement.disabled) { + } else if(nextElement.getAttribute("tabindex") === null) { get(this, '_focusNextTab').call(this, nextElement, element); } else { nextElement.focus(); @@ -256,7 +273,7 @@ class TabListItem extends Component { const previousElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; if(elementInFocus && (elementInFocus.id === previousElement.id)) { return; - } else if(previousElement.disabled) { + } else if(previousElement.getAttribute("tabindex") === null) { get(this, '_focusPreviousTab').call(this, previousElement, element); } else { previousElement.focus(); diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 4400c9f0..131f18a9 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -3,7 +3,6 @@ import { classNames, attributeBindings, classNameBindings, tagName, layout as te import layout from '../../templates/components/nucleus-tabs/tab-panel'; import { get, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; -import { once } from '@ember/runloop'; /** __Usage:__ @@ -18,7 +17,7 @@ import { once } from '@ember/runloop'; @tagName('div') @templateLayout(layout) @classNames('nucleus-tabs__panel') -@classNameBindings('isActive:active') +@classNameBindings('isActive:is-active') @attributeBindings('tabindex') @attributeBindings('role') @attributeBindings('aria-labelledby') @@ -62,8 +61,8 @@ class TabPanel extends Component { * @type boolean * @public */ - @computed('props.[]', function() { - return (get(this.props, 'currentSelected') === get(this, 'name')); + @computed('selected', function() { + return (get(this, 'selected') === get(this, 'name')); }) isActive; @@ -74,23 +73,35 @@ class TabPanel extends Component { * @type string * @public */ - @computed('props.tabList.[]', function() { - const tabListItems = get(this.props, 'tabListItems'); + @computed('tabListItems.[]', function() { + const tabListItems = get(this, 'tabListItems'); const tabList = tabListItems.findBy('name', get(this, 'name')); return (tabList)? tabList.id : ""; }) "aria-labelledby"; /** - * init : lifecycle event + * init * * @method init + * @description lifecycle event * @public * */ init() { super.init(...arguments); - once(this, get(this.props, 'registerPanel'), { + } + + /** + * didInsertElement + * + * @method didInsertElement + * @description lifecycle event + * @public + * + */ + didInsertElement() { + get(this, 'registerPanel').call(this, { id: get(this, 'elementId'), name: get(this, 'name'), disabled: get(this, 'disabled') diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 82c552da..4223f68a 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -1,4 +1,7 @@ .nucleus-tabs { + + $self: &; + display: flex; flex-direction: column; @@ -74,28 +77,28 @@ border-radius: 4px; } } - } - &__item.disabled { - color: $color-smoke-300; - -webkit-text-fill-color: $color-smoke-300; - - &:hover { - cursor: default; + &.is-active { + color: $color-azure-800; + -webkit-text-fill-color: $color-azure-800; + font-weight: 500; &::after { - display: none; + border-bottom: 2px solid $color-azure-800; } } - } - &__item.active { - color: $color-azure-800; - -webkit-text-fill-color: $color-azure-800; - font-weight: 500; + &.is-disabled { + color: $color-smoke-300; + -webkit-text-fill-color: $color-smoke-300; - &::after { - border-bottom: 2px solid $color-azure-800; + &:hover { + cursor: default; + + &::after { + display: none; + } + } } } } @@ -104,14 +107,14 @@ width: 100%; display: none; padding: 8px 20px; - } - &__panel.active { - display: block; + &.is-active { + display: block; + } } &--background { - .nucleus-tabs__list { + #{ $self }__list { background: $color-smoke-25; border: 1px solid $color-smoke-50; border-radius: 4px; diff --git a/packages/tabs/addon/templates/components/nucleus-tabs.hbs b/packages/tabs/addon/templates/components/nucleus-tabs.hbs index b705dca9..e0ba1844 100644 --- a/packages/tabs/addon/templates/components/nucleus-tabs.hbs +++ b/packages/tabs/addon/templates/components/nucleus-tabs.hbs @@ -2,12 +2,12 @@
{{#each tabPanels key="id" as |tab index|}} {{#nucleus-tabs/tab-list-item + registerTabListItem=(action registerTabListItem) name=tab.name disabled=tab.disabled controls=tab.id tabOrder=index - currentSelected=currentSelected - registerTabListItem=(action registerTabListItem) + selected=selected handleActivateTab=(action activateTab)}} {{tab.name}} {{/nucleus-tabs/tab-list-item}} @@ -16,10 +16,5 @@
{{yield (hash - panel=(component "nucleus-tabs/tab-panel") - props=(hash - registerPanel=(action "registerPanel") - currentSelected=currentSelected - tabListItems=tabListItems - ) + panel=(component "nucleus-tabs/tab-panel" registerPanel=(action "registerPanel") selected=selected tabListItems=tabListItems) )}} diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants_-_default_background_disabled__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants_-_default_background_disabled__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..4285e2d4141fc7f5a992f8bb6d620338bce75780 GIT binary patch literal 26366 zcmeIb2T+vTwk_O)coG)!nCdoefR*_pNWOHRl|2jInyZxS=4uYscXo z7z}2Y%(ct6FqkcM7!38(ZCl}$$juD0@GmOsThbRX$u-P_7|aoj%;gKp4k05Q&LN(* zWXfb|>1(B`ma^+RGUIQKq}{|N7FW2)r8SG=cxBe^x!Hvw^P6%pl5bj*G|%S>Py7^VM|J^Wb-URyLovvjO;W=C``Vw zdoz_6hs@ePagTXooi=U!7V5FHj`PnSWL|#8Xsy2XvVxi`LBH~VL3PkK%4!$o zrn-Szt7Bg^_G;TT>BxEXkY~Nq|c6`sXHCE3ytKPz> zd7eUEU0m*>;M)1Izh7`W{CY%FR$Cj3U7l;lEst`aU2N8Ne#1stC~dc@zDQVO9U?KFmablA!OU<_FQYVbc0>SO^Nruz1;8P=U1O=Z5X2%u06kEU;4ppX~xpC|(HN80IGn0A29zs}1$aQx*mgkoX(_cO!jlDhg~H#fI-yEapsp&u`#SsgdK4mQMEf7`Em z%Hr4O9oEPE{rs*b+)ly_G^el;7ryah92UCWjedOG)7sj~ovyh)XZ7}FGWg3GFjJV7 z*%bViyRB*Z*TcjEeSG!_ng0CRUmct~-|0*hAntfZDdsxW-7k-~6wLiJ*3;9o7|m?1 zk5y!AO46YD%wpeuA@gCQ!_0SSwQMsMHvGzI47;>P6mE4XQ9YCAx2CAwNSPO>940{6 zoK;a`b{~e5lXKsJ1A8}rDd$sNpW_)8%X%uO>(_T;(C6U|*8|VEU43)-otIb9iIjqi zPqyxIATN^2%gZBWeR(clxk9y@g+*Rjd7nzWa^#g)2QnTuzPxel@~Z>9Wo2c@f;D}4 z&OWG%C|a(OTpsBO>j^lmTUJ#SW!90$ovZ%$<6nt(ieQsBFyO*$-@YBL`N?in-Dzgv zLh-X*Q~{#4JU0$JleVf6A*I6WUf$k^giH>yVQ2Qx&@rEDHDc+nyUo9`Ls%+hXa>v+ zXAj(HlH79S#EDldVs>@8)})Dm_wV0Nluz@Y;NiJH@#{-(U*F_NdhOJ}2hS*$-(M!)#ExIq3c(T!-mzCY&&8@ig?##esuWb!>Yb~MaBCIPqso@a*w>^{){x+q+t%j zQ^Z)bnP$K9_5DVVo1hLlYgF|1=%sT#uLZWbT|B9l_SA8@|C96lSc=8)dJM~S8ASdv z9hE_Q-}HaX%a6^)&(m+ZlWEDOVb8o0qrK;zARPCBxbj-HK zw5&Kv(o4-B52qwZFI~#kyj)4EduJm1df1(iFT1pAxJC<2$di_Q*u6}3>DJ=xxa@aY z%R6%MEmt(#2$GmVrn$*B$MCn_s#SgdKkm)EAt~`^4aKP^|GfZJ$Hi#LCPQ{y?Jh?p zaC>Wur==iB$oIF(eWZ+!!*P)gC-HGRbzWF; zfpC^U&$rseh86=&J4+Uva7tp$81_p0VhdOQYvV7!DMnX@hC0O4^_Jt*6r&roTTR8-2P3`QTtxLr0&$rccZHv&Y6reh|xJ-X%^#Yw@;mQ`VxqU9wC%o4u5vy@oKH${7 zBH_g%DRGL(I4+<#D|E}*mnmPWu4uGoCAfBoTv^G$VryQ9$ zb2jZ8!Q2N7-xBVzwb?Vit^0UDK)bzyyNx~-YfnWAQ3;hIOo-&q@3)MIcseXrvS-H^ z$2`yTzrGw(V0Z+`iU z5mN*sBcnH<34mmt=eQ&!LVx`D!4OY`G9qNw?(n{Wwk_M7E#08{esi)Gw^qU#V{%qo zp6vwQ9@}@nzbbHM zsPUC?N}*qN7jF4^CYOBB*NN_8PiML-SFWHx88yU&R^7U$s2D5f&$si?nWt4%Ro?NL zP^e{-7Td;hNnyHm5f`B5uPQ697t4D4c45{CQg3gnUin_hm5EDDiE5VsF?HnG>Q)B{ zj)a%ry}>mr7K(kv@VWWns>(T2T!ElR3{0m` zc}(|S3 z<6ZQh#l{PYp)3#lh*Mfgx3t^=_@BI^mXn*i zA@UN<@17oXp-MmAJ}7(R+r}uSC?iHh&sTlo@&{1mDS-Fm8k8|*OE6(hlI_TKYjYNzp3EAHUt4z05R8eAw(Rp&ovIS3oq$`u3ks@$D2&q~N(Kv=Zu-?sBnwvh^K--9b}yH8r)JY?2{k9r@M>Wt#px zTi?*&nOC}=$ZSr=VqGN-st#WDU@nK{khym47d!(yNjp`!4+?##)MM9uOMMX_ioJfJBK^=xCJ6c&;c{o?sN-m!qCoJ%k@!7AE0b53R)e4<8&yK9vr@o4(rW;1>IAMt^1& zWtMw$TMtJ2TTOmrguJi|63%5vS@RwCY!DnIh&SqAwRo}s3{bx+;55bsnn>S;gbhgW zE_9A+De{6H1nW7AU*{L>q0cO$XlFiD*-FP;_WF=OPOq2Tom?wU$U^Zy#Q5?@&Kwpp zDT6Co?`2?sv_^XWicd7zu?e2Q(@hGVNOHGp<^hKm~SuMCEWhm=eW{AF*}p5 z>h8~~8sk+^?tjf9=GU5T*Z`%-k6#B@g&Te6B66^{qT)Ve)%^-ujQjVqWcjg+*tA29 zf))q`wLq;ngKK)NB-1f5HA>&=^WM7Ceb|!W#sq1YlHz9C9XtM#bdFk;s6SlUmgm{S(Nzx{a?U;+{rf6ayv~5v8DsY!dE?IefXgt8_1iT^!GC|PWO5%TER-E z8#Qq4-MbfP1gq#U$d$ZoxP_yr(3X2~VqnpGp7&Hb$Esfa)9)tNrRo3ab_?qTD+J9u znO*I>u);39{QUH;a(ubHIm1}Wau?0r$=~0)jiF)H{r&ZY`x}X$WLBd$V)kQEGTvM# zwF?4PR#qkoAswV#yv7!%D&Z6AqzG}?Vc)eYh2e;>us@$x-rpc&Nj6`3!D_RV6<{aGI*278Jdl?y}lhiYd zV(U~>w5#R#3ol>2detGh@6qzW_YYKeQtwjTm=neWt%GQ-8*@}Gm*z(*m-*KNk^AJi zE3k7IA*KZ}n~vl_*Zc$k%aLu5j{rRZERzmqGf*gB0Pq~R9(49N=5atkz}=bENm?RFP%M0Z}H z^E{pF-5(!setEh7#A84+Th7;|=n&B#FThwQg1c6@Fppt>(4gVY}Av08@pE$Zq8<(*Vbl*(xGQy;Pv*c#SeO0*7yRQdHC4z zW5<52OxH>cErf(H-`CakG&ak(A2$FXtLf0~<~xSRaeVvs4dvFeX6-o_`RBcp01PvZ zC9#%&nwx)M3{S_w!}D@!c9`vU_L=3t@bFq#ZKBuedJ`2@v2c6K!1{1Fuqp&WwmlnU z=3s)JE8qGyxwU!g-|xC|_kYvNI?k?ybrAn`IUGB82q;G7=wZ{A)JKccB>aA*1lXhQ zs9x&n>tp*ei8AO-c=B|(;_TUtoGaA{9ZMH^F8k@lqX6b5oMsS-2Oxb96O-JreeacG z6%w`b54hG!Q9>S3)9l^hcJPeeHb+Otbu51+QNn&q6Sq*zX4R}^uktVrKF~)awLs-5 zG1p>waXYqbj%`Xubp0p6K%c|^=h@zyR>vM`%jXk-}&@4My+K z)xplHJfml0C9^7lHw?41IK(|~#anb5gL zMR6F^hE>16@(OT11B!>78bH9X6uTxTC#yom=AT<1XOqH{UHEc-Z8|LYQ2E}96nT;! zH~C7(0vPbR3?n60{L18x+Sr&FgvEn|%@sgaP$S~7Zh3v^ZFO-<-0)a6(rnnxh(3gJ z{3|{o`Mv#k=L#@|r0>#feXH|bq-^3UFFf^MA|ip%K3L5*s#4!jR{jb#q*_H)>k43k zgQxFrhHMOEwiVEa-{HEfj=g&@kVb0IVgvZ2x|3&f9JbRhk`zURzhGjMe6Ub7JWRKr z2nJ$uWk{9y9!i9!b+sVyj8R3Rthj}H@Kl%W?B;Hgi`JJYia%jS`f{aW2nF%=G z9#{^2=m1=RzQ(*}7V&~KmHx1i2V*zZE(1*#z=q%Bc#4=em;8OmvOr%;bmw{;JpC91 zn0J1DB>+gv%gR2c`gwWng?hgIaYP#a;rcG!Te>HLo0F4j0zO4GQAEps%6ZP@{{8z! z+@Ez^Ca;*~bZ?DF^ey`X%Hxz*MXdWLbSSer2>!RXw|~G@r2{-XshRVZhSkB9xH@W% zsR|OLb%ik?fbq1H3UUtBDcp*K)7%IE6M4Xv{hvx%KR{=?|KWH?zQ1vD?z1*?Vkj)e zQ^gqnlzh-KFgC+YyIi5`1q_=D)rZPqAkrP_5h;Wv9%x*Cqd6AtuwlUPYGmmFf;y>{ z=Of(Z;tjAK@c)w?<5J5b>BW>zP-yrF3-?9s$ErXylSYkWwpj;sX4DvTnR4#%C|G2~ zK>tY6@YgPMQu!zh=?5tNIF0PF)8q~DFzS+u=y@rCdINxpAq9#D1_nX{GwmI|@)YE& zcQZ3Kl8e0z+AfpdEQY;$_AoFoj9t)9xXp{%v15nFIY}WQA!ZZ*@caDc=~GL?DGY!` zkkD7mM#lJr7STO+MT4*8%Ri|ZYu-h0$BJjb{J?Jh9;+C>quIDQdDm{U#arZj5IZnP z`gy9@cd1pz@#Ddl_9HEOA}%udeR%QmW&DKH(Zj-KRghj?(->y^toh~($Fbw7hjq*p z+1D3bG)tCd00X@?O~ub=9Gskc_V3>>X({P6^FTezts*qv=n7ps@t;9RIlRFs1>BNeTomZZoG(1H9zArAhm%7qT+k}2~agS{9;5wl_ zvI&)KK7iXAAnY)>Dev&z3=Ef`p*we7%5Un_azd(MEvt}u=XGcZ7XV|U3W4Y-$Sk`& zw?B3r7&F|hE9{A;KQpL~{1KOw1BN3Xk-wjP_+#6#<(7{dg)_lclgbIY zFMzyamX?+orfmmYq1k$-7$g0#%L^gnH<5{b<$1QlcZfKNxVSh_z}^K1GZA!Hli8N4 z{6|~UxmVtO=alo?&B$0<7kTOU$&;Ir5DDtj8_1JX6LW3m-VnyT+3lNrW~~(!6#7B3 z;pF4<>mnlmpNZA2qW1Umo0ev~Jm zZli51-0ARyRl@15zkez8FKUHOg7o4O7!2g5zX0(7pXv4P-353+8JK8LD!HIl0>EQc zE!YftrXloSdqE-n;yhmXr`zfA5OaVY?@=0k9G?y$?bzPAv3zD#5aYq>L10K)RBhQkm-;5*zFG_{cU75{#J+JtL_0`w6#!yZ{`8=*$x; z?0^PaT3UL72an3i$t?nI-tKl0aPdMaxd*P*4_r|cl%$c4{P|RRV*>+&z5DlH0Y>Y@ zY7OAYbh8f8vBG(6AV+}CL(A5!NfozCGR6O8H zfEt$4W{>*XOU}v9RandRnW;^HI;p0y>KZ3z zz-MEQ8h6%tUNSS|J8?VlE@~2NzF%1*rnLJv@P@g+?3Mw}=xFAX$KFSz2Hcvq+qjec z=hL2w{yu(b<`k-n=WT5E$F~e?tbc5V#i9zcdiTPfqY)2neh8mpS^toi0=zVMo6b0gr1goE@wKaPijZ!_Zn$Y|4!@H0 z31 z(XdZy?$cz0xYG)U$MDZ)-I)yDk(%Sbo1fIgYP98H63IvWr3&6&FvFcr+I#vW1D&TL6;J&+*m!=^+sy1Wfih!3yVaVL%I!)tCI05z-AUq*4~1^igZU?SFgTG>-Og1D zDg5FvU0NE2CFbu^NlCuCv90q?Xm!E~`*gwKBRw?G2tSPNh;-fvaie4VHusT; z>}{F$QiAc=>7;tvSDWng80%AUwihFHJ*B)>X=KZ4cqIo#H}b`}UxU!!PJ}__wD1=j zA@XmR^8PiM@C)?yJdwMjFHFc+ZKRo~hO0B5wkjtakiU8Jw95MrJX#qY6ZVP^Z^hPE z1SToW?%No9>?1Iz2qvgTr+$3?`2BqXEzNiO0|&x~j$d1UZUySdBj@Z+YTr_?c=Y7T zz61N)YBp{%6M7@=KU{X>LAtpAaBz(O^J*o11Il%9%@Vgc@>&IPB16A1IL^b-a*7mfug!40$6&W zB?ohiFYjez(}@$rw}CG861usk_%6#*7UXC{l53ILMhG0=R+p07e;Aqh*gM(IKG+T1 z>3gW)rHLx}cBVs3NzNCi!K@bx4z<$R>qbV$feeVZ__^X>%q)+l(+m1=kSdSy@a)ie z<>4VDzU~6BQC}#bo&hYvI*S9|-l#Etf8>pA$7qvEJ=qjZOils|GSWq4a|L3^9ZXKZ zRK!lC!@LSX%Yt;q4C7`Gu==sxCPqa){q~9xNoP5hJw?p`eC0e@B_4F-2?E%A-Qq5O z{P+v#Khht3{|kwT8s>%j!$`n84tC@VgOu2A+pM)eOin=|2B;(?9m0ix+Yq7-CsKS_FpU zVve?*ozN2+db+OQGx`;KC_;ajQb9pqFiL$daI{{UAFqWbzBSjHn^!&KZcoV@@rlpW z$jbPJN%)Le`#GR6Pb4~ma}!iucOiV1KD!gD$x*OF_DEe; zQ&tAfMF`aR=%}d8$7tUHc&>iDh1OwtG~3f*?+SqP_pptiLHGoM=}+(q5QM>B2G3Cn zT)qRK@H?3g_hSH+>rVBSgBq6#vIhdUg_mnH8a9`=u*1*jmh1#UaS%GATJhbdLRdY$-xjb4MdW`TsAL?Hs^hc)CgytJMF_NOGBVx1s@jp@ z<3oghxZyjRUuG$sICw0PymVPw zdb&l|^Ei8MkA!X!&@a2*2 zTw+f`!S(gqH|Wh+2wl-~j1ebO`H9*fMop~3(A`LzEt=E}sn9?yPT_&1{+@d8($6f@ zN|0t?!;tP-i>~QyV0r=SCcTI(`)CkTzAwXA6JSw1upXE znRL4X6I2pV?g#1fB=qz#P>N3K5LNQ*ejZU!eNOv$qp#(n!PyUc-MAWG0L``)6Jm~TNr z0ee=thz$06oAt+Stb!OduHGYRneY zAxJU)npWkO&C9??(adVbwTglfK4D?LS8Az112lf-1c0`>61KsTm=|IQrt>mo~ zr(KFN*|k{sg61 zy5XiIU1*e}Vq%_=R6En6b8-$Y%nyzMQ%zO(Nmm-p17w*>co3cp&bX1FO5^H40h(j9 zQ(za+2LyZ`_Q68#l>Mbvqp3^9B#@brjToFNw7@L2g9_Y^o$nA|ooRq1d>Y){#r<|L z6+aJ~=k^`Ifcyz=>Hjx3q!^i^T^Izs!Hd_gUr%UOQqM!HPMv!9%ch7CbMjB)l+zD? z9E%vCPIVt^^`F<-fOK~(UM2#UG5|4+*?istw9~BsST5||d_h4$0cm-dZJ=g9Mt}!6 znOm66Ab)=W*VJREw(j9raF0BB{P;0u8+Z&3Leddk(=S0v7)_n`^Yh!9-C?JJ*$fZ# z1y=P5q}na#Ef)LyX`td8!Yq~k0?y<4xp4^ir=`hYpMQX?*qed>rUmKbF%|XJ&FAX? z4Xvq>AgP`*X-S>XWPkwyAX-Sx^HsA3iyf#Zn~tC-Si3ZYsPzpX$a+a68dR61!hx~6 z9xf@d(%H4rFZ`v#_rw#B4`3i*#-V|SU3&%8*ohpVwU7MH^UNYjYv|%|I0r@7yO0yU z0FEfO1v?81=F7*MA0F>NaeFhM8Cur}9U>0>@d=FemoRZr9A|fK-wqk8=2O6asfNT!%_a{0iE7xAWdbJ3-6=X6?H|;TQ4FJP@?HUqZ zx@(TBz(5%|H84BeER|@cKPMn16%Kg<$GN<5KE-!3X~*9OWtQSyj3xitXaMU~%l`w> z|9j5O{A>10!W;&>4&tBB1II4_){r=cWa2uShxwrbcM1j-Hs`q!k?tT2)cU*Rl||_S zU<_*!(&i3#vAQ@59sqe+*@_nmitD?6Mi8V&G6?d1aEJo*P5?iU_fCVg39uOOV+fo< zH?e5~=3W2{FwhTNdbaZ@7^grrfVX8l&jbWoNsnK+giLe{K`S6?$j2&#BD^nVH-fnf ziJ{%5LB3$Jgo#_3;qK6nxRqZtQole9dF4O>XJYSALP`v<|0~ns6!Ebo0AEf_p z51?4^;SWs2A3G$B;}AgyH-7nw69Go~^kSrUyatrr51lEulYTazHH6p0 zaU*~(tgMhPE&{u4)SAW#OMR4=m)9l5MH^Apk7@ZLzsX5U-I(@i!Y{$a)ZRw;)En}X4 z=2ATJ*zn2YqM~CC`=-AiR|@009VU3y^!Ax(K?RRDYRA3YqltnIL&8Q4Cd)aVAzO)LTLHxOIP(j8_P}FW5y@S6A1Ju-G5sGF7gYdiMex zeF=vqH|JamREl2M_2@EZ02H9hgW>d^S5#r4gl@^}@<%kR0VO3RA$anlk(pU%?@~=o zO-6RM`RqI#SHxgG>y!%7%)nmcl!3D{F&yP!iVXtnbrica6DJvn1;|c%%Ajy9?Vho( zK+QRJc)2xZ(j4M=rK4+A3(EG}rlzKo>KOy_BpVx>Uf2<3r%Rc=p&N+F$`XS3bDCX% z!+ZX4HptQ27_|M+uGKO7#no|qI8-w|aBndk(AEeBgkkvg`5s^@%0ZV%hr?#_$so)> zFv}m!LSPr#b7wCTho!<^=yIySG8GCtwh#KaGKfDo4VVokMNSYmbfIJQZ%`29&CJX^ zBx0rajE*I*d=^#L**M3wC$=jvr5gZgTGay#s^}o0(Zh$bFujuGNl%iGl7bBUtPQ>y zbmVaHN33LKhxY(jWt#%SF_1@<(aM=&dTtg3(8AOIv4W<*%m(2OhFfe z{2VAMr@njQ&c0&gSCE#LMw<(C-P2N(#o7doA0`UGESzze_*G4q$;izWfp^vHk!HTR zI$n2)z8cic5G2l2S67?wm|2_Q0&7#%^OPz>NpHR4$}^r~V>ukXLi8rOM%%&%mBXht$x_#wdga1)Jp^ms)PEwd$~KE*v~Q z=LLvKzq7#ckc5-X_NF3K|+3vm`*h24Go3N|XTX@{G*PXxKweF1pIN z9zmu;5R=h~(W%eW^u1slw!NLA9STpzE`y>D-mH%l<6EoKP$6ptn>D?cmX=V=$#`km zcVsRV)qrFstSCNt88??64i^>#+0S`)`#e04ijIL3!1q`HPm4v2gw*l{CW<&%O}#i! z{Pvc6TX(xL8bIMc1oNc;r|CG#k-ggxZs-V}u4$0z`k^W$;+mm5of3K*l4GbB=&anRZ^XD1d%7T227>HuizytKd2{zz-E}hXUdz+D! zWdI}wk`~Y0|N0!QhmoI1r0p_;)2sw08Jfk<^!8PELWhY?L)J|*`^VA5q$kO2}_MB z{YX0mkE0JgcHoi-So2XhA}=9uDe~|XxQQohdj48=);#@GcloG zp@Y^3-&PrjmeY&1cu6!20{F)$(L|-SY3d zfpw~+qG5iEP8vYM)|m}~3vf?b-9*E6wSPHEH?TJF3A0o!=ZHV4Bj3%HZtcY_vu#%O zOUcQJTFvilM?(kGdT7^PAK#?0YhXA(FU_hk=m$q!N{albP{B2ZwZDJ5WU}nHEZ((u6bw6LFt7czW3$cL zzmM&%qe4GKoIk&I5!X#rYoEmY&);Bo?R<)|mvn!Y!G-BOmdRPM>kY=0X%6KtStNF? zT|4Uw-Sz>0%0hI=bSQ}Fh!GR2PPvlHyx z53fefM{8hLH6+J=&UsWla2ercV?=NJ)iIXDpV#(rOe<&E$<(x3L$d#MiAY?MBV#9M z{P48dt#q^lGHLHK1q`)?glqZkgr-Z0or-mNkN46epD@N>$e?U^0jy?yRMUTD?sAUO zvFRr){-P=r=(A7Jp9+;fWWT@~INMtxK6_t7a(NQBxO~aJ(y=QzJerqfv1D+$FW_cA zmkcW|PmnU6L|dPiC&0dSzFeeqS5@+^j?OvT)v@l<^fY~2_1l@zl(GC?26vB-tQu{d z@7l%)QoISbW4p}8%VSGiIBab*jm8J6%vl}I8aHPe&IZ{YA`@1vCzk2TMq>L(gCy;8 z=lQl1?k;yN#=;htPwM3IP?mLRNYPiSjhsv!haMVFzZ#@?Q>KYheoJ|I*;p)^1kCtW zv6=6EZMe1&+LCc^MYcTWWCFIh^VbPg+x}M+N&aw(lyiT-Oz{~LO6c@AilKIstxezO z3u3X3SEOTBYrR73D>_96KjL!ruUg~IncgH!`#zcK{(_wf&o(`1*jnMC=`cE-JGXLl zvGrT=R9ou_tLeg&!ck+VZr>Pjij`6c>*V*>yC&Oc(%YO5TeW|8*Z-PIJw3qIZ+M}>%f(^f0>qn)aof)yO+R{j*jDd0 zW!md$IXhovSs|RA8T2LRk9%3-=ZRmf(ZjlC*U{}7pezg)p3w^rwH}xtsCD_;*xa_F zFq}v?=vZ=@aP5T4d9Y+UH`#NhhusP6rkRt~GCf09ayBTF6eBgbkCB(oIrqChl9(=k zm#^n2C+*eSOwV7rlq{RP)GC)dWXK;Ksj9__9~;FR4YKAJau>UggeS|Y`kgeF@k&%{ z)0V*fl62M|xUg(6vWHDh%hWW?Ir=y(wBZ#w4Z0S?T8{Ext2CT4{gT8ZGD5BIqn%gV zD4QkF=rLjrwEl0LMl-t02L*?lNRx|9VNJ=(UO_>^?l~cE^8rw%(=7>JB3{y3z^j_my8LqIi*_tcJ8Z&WmL{YvQf0dSWxD0eZFDs zOxvu4jF(%rTtcbcSgz7xr|}RDSj?z|1o5qFk8AG9a-go+o^x0S|2uEWiD_&kvG+Os zti0EX-5l6(r(e!x*xU4_b~F7oy)^3!KNm`tJfjuUVKeNh^_4xdL=E3JfnV*McAN=R zS)5Ec5bLnX*}XKG}x)xs&FjRk2q` z{pM$qYXz0Gt7+wp1r|MGTzc5%s#8~;QHF7JFf%^DZ(e(W+PUAo;Xq$>OeUIWYP-=e zV|?zZN_Ek6yN;#Tt2+6yF<|AGtN-AfE$@+}+2}_obdDwv#^js%bXtQX3+)xK9WkZ1Q?!DEengQJ zBw!VJ$BhP5B$_(E2aa{X>6g~E(z#OYP`dR`@oIV3Y01B5%b!90uNSU=x%AylpeoFY z;nMQ0(9z{Xcf(A0+$3f)pKj`87qYdz|{-%A~{veDSNB(DTnDE#&5E>d9C}gS*&0t}xHJ~np zx223%&@m8j@BauFD~$I&p^7l5TAE(Dj(Ag6;-Yy6;&afKzYGoy{rs-9Hq6dG@(|VP z%M;1qM3#TH^N`miIuQ;*K|zF=j=D2@fiW3u;9Nk~nj-M%0;~o)QJB;E2~MKB?$k;1IV&X84~l0M2x);aF)_pkiJ-HZHw00dI)J|t0s0v<)%PqC z`_YX2(i%%)t{4%luE*NX@L>}AMw_9bA+WpkCVALSR|}K6 z-Pt45%|K=T4wexU%f7LkA69z$@B5GTERgCBc66^)=|)vP5qKgP#synyfZG z&_%@C{WOXl&5KD$I01Iv8ZO0P^YMckX}rc{jWke4L%`TQI56-LcUwn?KWuHYk8i`z zqC*J*ysYEEaDxpTPEiJNFuk7zy~ziVE3@s7n+Vl<_0AJ1SoDn-%(#W_Mq^-NzJ$Q- zP^Sh*enJz#y)54Y{JA8c@amh=EWYO0VU5OGJPf)wJ$%)KPNerO>R7*F%Szuc`6CVj zeRqu&$p0iDF+SElJU)Ir3SNAprH=7WwDGbdYvt7$Q_+Ambhv?A9s&FY{&RkNG zhzvLa-ognt0mL7{Va8M!l315a+C7aR%2EoY0?s^5cZ}nN(TNXqH4p|2p`8zu#5#di z<_Eim1SY#Ak8Ct2V2^IpRn>OvGr$XLVOwRIDf5Xxth*;U|$Se1wlv#l=E$7 z5MoHo$fI3kULX`JfF*s&|9)g(pbRK7Yw+qD00S!{bh#G}4DcgG0I?C(lH19ZI^;7T zs#buFz^V&7-v>Q?5~~;($gPLsV*3-*Ybxvs)1l%sb(dtk{>NNXcX(!hR#uivzd(37 zT30yocnnnBDiGKkHQ^u$^p2Gv!lTE7ZbuxxWeHsG{XrbH z2u|ig9`o)}D@V}`S%2BW*bCoubF9YLdfr~n7?c(RM7i|FO062P0-J)GL-0?P!O!nN zk>!#hOujMiB`lB0L03dLX4_YmQ)X(WL6L-R$my@?Rm$po5E(nFE(u2m$ z5vWzvC_+}ygTwTQx0wP1IscnCZ!`)@QJi5krakBfKz+cWuN8XDCM5}SN)3ctD90f< zktsR#&QrN|cwpcfFi2>5fMz3wQx?Mo?tXR^ev=KXRNrfXnrq>b*!XY>NF+c#)PR9_ z8fYVummHrAgle{N7#twdf)g+P_|;~-ycgp0WUSy2vjMDO4Ul|Ra7D_3iU^NU-)u6$qE|#nDlf3IGBM2 zGCVlFM)X8dQWE4^8I+8Hg0Zo)>*tnQl$m8?M+BU3wUHc}{DOh^udLcFOZ!}@>8@e1 zSOOeO>VMAeaV*)nx9pwB(!dAd#SuhC!s>Mwfcd@>s3{-NDWl<>K`tEpL7}(;Ul4}% z>XnvSx`|BIe@GIvcbRa;YqK0Y53_8(vlWh$fU0C1F9qCj;SvQ{w@Nr+iM++Vz#ig3 zPxOTYbhuzb4?El9HTY&OAN+zlz6@+Z&#fTT1W6o?&+)WwLD_*{X%7kWCHOJ z?Kn78e)XZTFn(A#yC1&bND3lE4xVau;hi*Vpx&j_t_4TG)|!G)Cew)kgohW%0cZ7)GPL$#XCvKb~@CQU`y=bOb$xO zjkV258ma|$5P#Oj?K!(D!_8vro0_s(O|3f55j{LS&I$_~0u>V+k`9FyEp=OlHd;wn zsb(?q_8HU0*498akb81}J*Gu$*-lpRb3hpm3TcS}2U*8B%7HpdZ=Dt% z3nlQv8LcpI0GNR30H80bx~8nUA3#p#lwrlrd>=9d>P5y2s?KVgL?>8b!Z{EbG@I^5 z%h(U$W2|fSFOKaVdRvNTr5{KfW*?6q(D{%RaXG!Oq{&XgQ|`#jw#z@ zaAf5ARB3E}t>|zfM>!;L1Q^f>PB6xIbG3Py3SP`NC<9Os^JNf3kuj?#d~JKzf}3J3#_R90b`%^i>ZkK!huy64yCX2#@TW z0s@OKhcAp*1?WO`6v~`3`jCZu@r(ZWIEcxZDUcKokg$+1lxwkYgo+0W(*qb0K(jdb z@{9DT+*)ih48>-kt^%T(2i-8lwn?oq7rqZ8X9`XR5RKYR)vTj$#ldk5;zy1+X4rPV z!0X>8PqI-y)wi~)!2t?el+-_JOM%K&(Wt7Uwc3doQeQY2rh<-O%G_E6CR_0{o44nGWtsxI&d}aIMV#DH-4Plr0f^>h^+xZ+MbJNw~ zZ73WkxCDJGhM80-yB+|W3p_snIkXPEgpbp2gvOc+;Z!-YJ^+K*b)eSV$VleWrDD5k zQ*dCQ(m*FV5MkO$aj_N^6PpGQst=Z{Rz3cCqD=u>h56h3tv^>g>JJA81>I6r^#?rK zBj00IxvB|M1n9{(yO+F%*-c&%Vt%ZPdR`G5&K4v~&I! zxY&Ot>Ys^%ivM58f&3G^Yw7bpp_BX*oBzb-Ke72w;QXhB)_)SnKMCYNoj`t3%uDGu zqY2!+sKI~g;yw8J)aF0h!p=v3JP4Q8O$odIs0jZZ{(Shyi^CYqRv+}=2w2-){|)Eh upXUZ;_rKAe|7X4bS?~W2x&8n0ea_yQo$hf~L3GU`By&aKa`MHykN+Pqk=Km? literal 0 HcmV?d00001 diff --git a/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants__assert0_0_document_0_webview.png b/packages/tabs/ember-backstop/backstop_data/bitmaps_reference/ember-backstoptest_Integration__Component__nucleus-tabs__visual_regression_for_the_different_variants__assert0_0_document_0_webview.png new file mode 100644 index 0000000000000000000000000000000000000000..e74c112145fb8bec22edfd5c0b26e6ee36f98f6e GIT binary patch literal 26409 zcmeIbXH-;ayDeIXiV@5h2->133aAK3HX#B6DoL_R5=oMCf!c~0EhI;kAW4v%QB;tu zAUUbzoO7r<7eeoQ$NjO--eaFT?l`0S`??#8RcpQRdFC_cd{;lbC@Zmk&CWF>5^25U z`LmZvq!r&tB-+1Mt;8p_L&z@4S_3v$T@OQJCrw--k zKanr4`FdeZQskxH_)FyIPlb-s@s)>9(?3?-k{xhZt#8w1r`ksPj2ntGPxnb0yp2#Q zDc|WN!&Xj-ezrgF#5x{5@0PI{ZEI1(;g7-Bd(5ceqSa&Bn$kX@-uYGy6^`)n0d76cOm|T!%7o(oOK9A$%t#fWH zB@Z_4y%MjpS|h_+9yihH18T`e0ao>Y3GWgY2VdUs@t&%m`v#VDm;6Lm?3X(OdV zT~#sll9e8d6XTQ;pujMC@xW(V24T{NU8b2EvjhG8FSyb(KX7wnKYH}2#h0zh zM{fQ6u*Twmm#62sD5V%uXLT%V&g2(vlI`R#SA!pKH`dnHvL~o7j#;_QS4royi5Mx; z>{zVximSEpx)%b4eO|uY!e?CfxxK_MZKBD6##`9#n0(j;nyVl0tjHX%Gt|-1xz(3c zT^=sSS{0*4_kr1_;Z)MiO54%z5-KSs%&g9{M@f4no`sO-rlVDpIDV@OTK9gX?2{&W z^P95BiHvO_?c29+%eHNsmVYeZQeGV6QD(Dp8v6?u){%(suu+!ItmI3UP*ayoD~Y z_ux2w<6CgpoTc z(auw!?yslu7PR8Hxb41#d8t5096qPGyYJvL+Ro}Ux{;2a=|rsobNe?X?xhvNTqH@? zX__^*?P8Vairt3}J!TfN{+4FZG3fpJ_3Occ5$;1A9M=bber#=P8|qCc8}5ALHjH&X zuM@tsC(9)TBDQhSF|f2M=S*)snObItzMPkp-OtX>E^Lzf_iCf&Z06k!u9>qzWg-(y zBkd(E57`5QWdj6Vv-S!K`ieM=|Lsh7>ZF<3(9sUP9E+t+7Q9t($=1$)_UWC|>`=bC z*_W-o4QcKx)^7V2BDtA#fBmkNF9Xg8@fvQ!RCCuS8@DuAl&!ehob5z1s!v|;lAC*) z#ocL1s8F;O|vJ>B8vuU(qB+;&17pz<4 znP}4J>t~wnQ^cL)?>yVWy08g`&bZPOTOwL3CveZ1`*F8^oz8P(WjHA%wZ@%YZspa{ z&hNt1*%5Vzz9cp_wp%Sv`Hz{mo;OMF+E1FFpUBzF%3AjI#omI#!q=znu0&XJeRan5 ze#cmqnkkwmN4iySobtlM;|l|Mkk^kJ3wzwJia-#bk7$KL8$ z+{2R1dar&Oc3Z_eOqf{r)qT0gsA>CozN78V?yF^iB0r*(8C-gMjY@?_iU;TL!|?F% zjheVUJ9g~AuiSij4O!l6nwcHNF7`Le+i|k?;6!wE^q{NE(rOU&jO9G(wfL+qv06!| zvkFT?W_pO$@}H=8X(Ei}_ka>l>f zUHJw}T?Lv)qn3IcnTRh19Usj#o%Z-=SEb08(~1<9_AGV1LVYyC-ppEdqf=g1zH3m2 znR4^UlwFDisbZ*83YA_lS@;m3D{^@)s4PPPeG`e&u4b zo1!O+yj@M39{4GfKbfcHOW9OKjh?!-88ewbHoCOmNxWgIW`Fw=!;inI2IAe_jlu~! zGZCth|2+2F!L{EcMsF1F>!|+C?o|~UvgOeu+9vf3SK1Z(5;Kq7zqsn%z;-#wgCQ|R z?GlV%HYlzz$xhzm<*)UaC7^{SHA!MGmyzwt>O`T(A_C&1%ye`1eW5h&flav~;VBox zt&bb>&wRhUG&yYn@!PoCZdN{7_VeVa)rR~X%Qh_Eu;x@$*ilmSY`*4v&#?Vj@q4*t z0hdG~PY3LDoh|*!5iHwxW4@#6?^uWFr8zQRExKe<#4S0ywIv(?IIqU?Nc)hSLRO9%Vo{35E|d& zNPEbAaS)x<&dPWnxt7a6l`AGp6&N6Ue@YHmar{y2$6|4{+TgkVId{^4PKI;Li=0!jDZTio%ud&wKFh2kGb{mgoLm_)TL!6ThO^3f?fLQjR zK*q#Yv*6&rdxY{fuK8%#m2t)2^NYR@o$akjafMiGRp!*CT^x{i`qV}8%GuFNSGGJ) z3wJqZ#=Gj?Gm-i8>5$W4Cj;U)2$9UPZen$;N%pklwX{lQRa!Kbe#+$qZ?jbd=Mt%R}T1k z`W5j9pdn-#xdU6Sw-@`2c2_<&jLq^)X(rD+NZK#s`+4x!CpQOradC0tR|XYf0mYZk z%gKdHdvUGXaqREn;$rv6BqVC7n5p{yw2naSZ^5UK^XC-g7L#SUO#?qma7SxvoKC?e z(b>T>RnaPEfS4N7t+Y#gd3$T#-_2)r_J8~K0ARRPvFh4whp&!xSLTbj0*g|7d zh&eWt^*84Pz1PlP_AS|14b8G_;Qi~Nn_KJ;-yCD57}YQS0v5SB1F0?a$BrF4G0|Wl z_bYmFwz{?~@2pn0Sg)*MRk?A1VdwD&xc*X>{g`%DBr?5KPnF0kyU+Rg@^8*PZLtqa zOniCi%{j#r#^g-iXJHslzJk(Hy|2&rY}vZ?UaYu;#IC@_sTbsQY@xBwf5z-)pM$3V zfpW`V-m7ZKcVp+{^Zfkz?D#-4!v~>(%uh(moj)SvCu=dTegq_bry+zH%^&TmV2IMX zd^r~=L-N7}orYAiUp*EbrJ)jTECihJF2`Y&0Ae7 ze2%p}aQ1uCH}Jj6+t*hr!@BQAQ>MV&%!K~j^cVr6wsES)nYEl_vHeLSXx05W@9C}` z{3cBA-@o5lm3jTkn(5K5GZkSnUEf~O#^%fml5m6Tg)a|cL@Nt{C6@x(&6_c}GX#if}pouJX`ge}T;hdUj+xPMOoQ2ybU`$Y-`0jMAEW z+8cD9DkwRq0(NLFJE5v^4`}s5^j1#88A0qFCn~ozFeC7JC+fJLb;Od zD9~4*!qB`+z?=>J&<0Wy9{#!)6~G!7K)R!L!#cP}O`^eOKYr78ls6);csttK%;v_b z-MziPVu>P3FC$PO(A<7*Znwq^8=^mV8AQ)xh5H=UUI{2U+FR?cA82KykViCW^EqI!^J#hgLvc%U*AIP!U(M# zQ9nN8Wk0KP=6Q>}xY_Zv^}G0<`}s+4SWa>r{76SU=HX`cVSI<^U%GW)VZ3fp&3kRS zAoBD<6&00rtfKz?jTsgM$~3M!US3h*mY%;D$SemTHjHv+AA$k7%PxOKm z3;?NaU90=c3^5=iJe48HQUu<&=S ze$YOkdo<=U=DEeUW3!y_smqr)<;=Em)6`_z@sLoOy~0Kd2?-f2JQRzdK^y2iZ)a|9 z{>-7IOmyb(K=uq53g{I-@9#1RR!cE?vWx#2hhpRr%g&1=q6!8m-f`qANh96zRcfkt zco5ka9pf>oIbv>MQJZAQh*1v-4ZWnUUW}^x6P4%suiSgCC0#d&@qNZB78oevP>xdK z^_w?#z3=loanWZhE%H>GWnW!VZc>3eyG2*1m-)~aM#P2n2smdEvL+gBSg|=eu-|BZ zmO0P!pnt`gwBF26bjGRWs?P;!1j^uTOUH zq_$F|ucVppLllbqA;gu@du%74(O2BjV$L{D@A ziD<|PZ?S>Q5dzU0*C*?uY{r5ai4$Npc6Lk5Lt&5NMdg>X9`;pIX3?LyW@pH*G&7gt6q60!dYvyf+Pf_?>3kteqnxtQGd!cky* zSz+OIMAfab8jM@FGADcP6|ihTjzS4UY-*k|;ms}yWum(d2&qup<{q?uJKE-y*HalK zfg$-+y>`u-zeF8E=0wVO7S+c&rUgi8YBp`GZ)Fn9dHwn?@E=!FOf>Et?eMpt2V>!( z8oSv1fVUO%%k6cyGBS>|y33hkrV|V*_HWv>2{eLvXdq%GCo6e!50PjKDEmm5^p*!L zMGoQ0=l;~Y@4m(B{;77e%6SWUO`DipY?__;9XYwV8C;~f(ppn{oTxL+bXSLdfB9vI zf>rzX=ZCIOMe62R44w+v^oK~g??0@W={-9;JD7!V5PSMOYlO1M%K&>h?88Qz=Jm0g zck}amacQLg12U#n1IkHr*m|w)?Zen{%+|4)EAn5(oWS-hC@EP_EiEXxhLZTC$|?sG z-*GkrnqaSpP_lRYB)6A`$5wjXiN5;$$svI5?#QUlrYwil$^x&-2zghe#rGhGF3Zb* z8aZTIds$t5-Rz9zMBKGcq|Tc6dod2b39bhn*;+?Yur6s!0(hW(xnhbtXZG0U{TFs~ zaB$43Pi4I7JwEm7m&K(^8%a}BQ5QMjb^SEXqgrP8g zn$lptmW0Zjjh)@1CnjYRBcnu&YEo|aH|1E(5^3(Nv**s8vwhcgd#3aI8=5O|S7|Pe z^E-pqA!x0O<1{sACVGozxED7f`{5JfSUHT?)4Zf*yHim$-vwaVz3R?x&=X*pv`EWA zp?n13*>=JA_(9SgZ*T9brTzjRk!?SfiBA5!dkTM%mLtrrL(pNi?5#OES>HTwUE?4v zEq%A%B!iQ5Cp9&dnAML6wRpztaM4|4d>)LF_8>Tiv;Gs<@VVN`F{)b=O`AnYcfjVX zFfcIq=t4ugZ1)YIp*Zul!rGv06FAFHfPZgMQ7k z`$>1OKIn+%wYZ6~EI+lus3G-eW+1{Y9VjHFdmjK>$+=)@*z+(gD}%nlFLV0C`ls;vGo_|)B+Y^NDz)fqp9>>wLp zn25kfi>;W;C#Z1%hyENYnPieVbPm~w*pE3gjRT}qpwraRa%q|ySS{ef+K}yAyTXIQ zkV3E|h6g_}xc2ldg8L&^KakEn*?y_3;2BZ_QnV7;%Hs#p1G3Sp*vx+ZKHCZTrS+xb zar4(Pe1Qbuxh1z zwN>X`_Vi6DDXA-&wnu?_fK2FJLPFT|$^uJXi$4a=-%RX>)KVZ|OvU=4p`qdcp@|0; z2U*3O=N-9He=gg}dsE@stl0bz19|AN)-5pL-x3YvS)6Bw)|7>Zg%McXm)}$tvVuwu z+38+-8*$mU2zhpNt5ky3X*oe3A|3yXjC%LlW}s;nOku}&3D&l`iRO-!oHj9`*&#+1;LaReCgaB!iq8D<6d++w&UP#3yhe&pwgHhF+buwjRGU zpSw6kA>2`;5@(C#{HX*_|8uNbEBxB0`yX=T!g4^v&UQ+Oy~Zj)Vf7*)prkxh`ryHX zG=;KJDaQ4l7*9X~Sxf^B$Xg<{3L_dP8r4!Z?mc%4l=R-xeJF+rLO8`#3q{}d6SsCp zDaWo1mJ1R^rR56n{yjlo4y$P+y<0_jdFpWCA-i@TecOSoiSsftF}GB+8&HVV#$BTY ziffZ|p5Dx;ev1Z513|@J@IG?8TFW^AJTe)T^rq!BK+lfTUExGIg_;1+8hpO4nghn~ zyVzw)xAs8m;RL>E!F1@N0&oO1#KVz#^3ogh&ePz$g{Tg0iWb$LDmVh4$ke*TtgJ5wuZ_jdauo8Fp@!KZh5zIpiQQRJZ5o}K(A z#R#u1@teomEVw4J2Al@sc50c(?OmL3!DK1AP7-Quy285qZ2R_Y+`4tEsF|q!=nd6m zqaE~~Ma%;dVnu2}3QAy3P`Isf7Pi2!t=oRYWvC^8M2tEj_RFlzSf}tzpE*L>7Dh%_ zXMzbC9UZ+jsoC#q_-@ahJ-5YXe=#6k5RjY)Wmc)sfbeef^BPUN=1~W((t7OXM#quE z-S*Kj?M!omaXsJ9X7=7C9$YJ+MJlSO{v~i*1-2cDJnSB{Ve{rQDCkaH5c3>9G7}Z2 zU&g{`+H?U0;Uq9Nks$~=3Xx^K+v+k?^p{}GoIyn9~V zp8tOQ=8sheW@_FpCC(@-bsBqAUx2(qW@cuI#`W7=P;A|o3zN9nOd*i*(^t`L1?g5j zS8~WA5fKqkz+U+I?aJ0-dB-|k?A2GBz&`up`#x#U4UCNW-(H1qekuc4X;-#Vwld0MqttmqR>UsN&H7P>2?9Vb01SfAil@G} z=#V8=ba3yP*RK8I*REZ&%_*FiAu3fuo+PS{so^%FTEIsj6*q0;*1Uiv?N+x*VkxVp zw)A){Yp`u8Cskco@&Y_v$8;j@bd<*~lhoTpQtqr|UQPohONay>sRL87tWfs7X+!D@ zt=Zp`)7+8G`#F%??oKxrsP|yiF`){wCYIEF$*Ly}a^Kzo=x{^D+nzl1PY6e*y7; z&!oI~aS9!j#6Ux-+>cTTfXBRqw;Fn;KI*ScP)I*I3}pSOc3PeZ*`mg~9nTQKrIqdf z`}cJ+1UqE2>Or(%2yt~IGxPcn|1afj*&tT#F(d#i@;6XXc!%>8@!-u?bYVr008y8?yh8&N6% z8KU+X>UZUQ&Zc16sbDFMa2)|DDq2iG4Ko z8$S5Tr0M8v^?y{oq(N9-2xmL#Jjfk%wEKl~ymisM{E+=pd!BW@T;7?(V%Zd78?!i& zk{;HD_Q4XhPD^eY%bkx!J-D0w)QX?e@eP$daXdGE!fRLT6S}KKRkRo6=I=ZS&=o1q zXAp2><%^IRZninrQ+a_RSgGG8HvWF7LfC$m?O9J_Z!`153NzwDW zy~Hw~pE4mIjoEa4d*z6Fcb<}nZqHH;HrH_?wBrL~w$GPF+FcgLGYQ`FzZprr^;*R- zInBi~Vj_Mk+#Ybg)Y_u#OJ9@2AU>yRx<**R{INB@dkvo3Sm$d^y_u3>%9SgfFa zMQekvy494APRACS!Rc<1moHad2$C4ZH83sJ zv9u?r#|3q1>?IGre7XE&t$~Hh@82$^Vv)2T{0t|SJx@wm5GWHTHES*Q(cBXcrAA&F zzdDrD{gO(*?8e01DCTea2mCy9f4Kq4Y?4GN{>J0Y;1xbtyR9~-( z@`-MGS@f*;G?#G7ez{Pi4s@=!M*i&G^h)oka=!7_Ef#UXfA@=tKEAlB=}JIJ)FGP$ z-k#kpbSQ*xhBv-)SlZ$S2evG4>&V$#pJXG(8|gF>Q@-}`GHV^i@;I{9=~vorVw5>L zsjsD+qFsVZ@nXQUg6|(ELYG2((8HxI@;@%+{nuo|kErW8UTp|HH7HZO6lOvy&W*lb zsSveI=F+93im%^rXe2fc+Q{9!99~}N6C*peWvT7qw_#4+9KT>Ao{; z+ZNP)@cd%GWocjSK4E=W^RjB;?YrVzwr#C1U3$o9z{QCFaM=y`YLoxN!7=dH|3<6E z9n1tb7uS9u8L-_fW*6-31%bQR6`VobgJ~K%8_t*v{*3VIz0=5$O;^&bnl(QR~> z_R6^k=U!y^Uw@WDmr(yMhHtF)odhRB6t1XL{}3cGYAO4nY=P>AlcuXOijgSROo1^s z8p`+{fB5T@E9(T)qeqWA@hhMaA)nCafq}9>TLf)_>VaL+5X`iP>PSi>YNh-|R^%Tl zBxE2JZjdstuHc9r;a!VXNx1Lry&W(z51a!b#JGvgpSX1C63VR}@1$*quYBmPjDoz1 zqHiC}Nv`0SQ-d0FoW-E1t_TK84_dM-)$r^lR#vSDUgvt~Qjbv0{q5Xrmd{K$+6c)t zS7m7n99;D+Hm!XpVdmT0#CrV325_gZk-_t$6*H`jyQ^XxPLIH>7Y>J7{@4WrgM%Og zB5&2r+8Q#+5W`799S*7T00+k!t;f%v@d+=w04!A}^JqtbMOenj;Oz}6Be%Y~xaz># zn0z-@IU}RPU_pADb68zK47tMO1g0WPr9n&q`Nag#(l=AX=z$o=S)6Qag zVxSB~d~KQqJEv;m)t0=c!h;`Z2`l5%UHr#P8cu-1JQQsQ=O$ELS3+Wk97_-bB*JM0 z`Gc@Yg0moqxLv!7oT(GetY+&Mx1@qfcps<|8UGV6CMRtPCI;xzN5EqL#VQ_0fOaNf z+ato`&xHDa5RZ&m?=fVdyJ~Sj``GK`B0G>k9CRMne!0L4K!|{ygdu^B)$uu2_%ZFgbr2N0P#Kj8kE|n@#LSV> zmM}k)H3KqsS%8zbAVS;%>K>V&8IbU=enD`!;EY=cM$kt;@E0Dr7sHKRC=b>P)z9(| zvCeaZG$f0s?cy^c1d!dUNFah3I`Npz?6R%+&KvX({I~}gAIQJ-qI0A2gwIvf@dUKk zj=Wu_IuO56_p@5LEhf{hE3z{rCom8MFN5g-FZ}_%Ae}^|mDU?yl-q(B0F#40G!23z z!&p{euYc;Ue0zyt-9Z#=&L00r$NC)3J7`hTmX^oSm=Mf!K5rR&pzR&dR|IdgEY5C3 zj}&&n=QK9jc}dA%9?F`p;Nv44f32;pOcTB!byb?2CN!qU`__JdOm-4X$9Bl(@P}UO z{Pv3Ughj;n#a;=5=S10i8s&&e4PzH8_W+-|JX@t4c+m zhG@vGBhDbHzsFrWQB^6z@k*(*Vs~+EqEY0{=7m$K|$eObDPMgB9XBl^2ZIU zo;dWUxe_(fp7oxq+dE}`BoONdQI*3# zor{A*(1=sQyfYO!3Iv2WHZ|yg{UrS$C%%i;@$*G98w*}(7tTDXh7%J*azB=F%a*f_ z<1h?_@Oq|Pn=%*j$jr>#n_M7ZNhahLUETLkn$~DTrDXy)$dRPE*nGiD;d84;3pu#B z+CinYVy5+w>tH?wvDqpmf>^0a*mQ_QyI_Als-3qn-)llP_6dmg=l=fcuXKz4UcDK5 zfJ3O^OGB7INZ@KrCRMRXNnYya1!mPV;G-D`=j^BIWaIk0&kr{<>~csXL|GfKpD5Z1 z>KqL8F9(DJt$WW!FUQ&+5E=`_J_PTxOOyR1$~^EqN0-3aXp&$k%`~#GIH6um zyS2~GsF`7VQozg12e`X`T42yNj0ZaOUjw6h+_>%~LWxB0ZB4F2hYm3n_SdJlfeoX* zRjH`_9fH@bD0l8C`EdH4su*n)${}H4_dArE;zLtYw@*%V^@FLV`SxC0f*1!xnId$A zBXM6uuWylIi4PCmfwjZ13+Ms?pTs(tOdGa2Q)@7M=2Hh`X2M1cr^;F|OASzg8=NK@ zh37^q5QLAy-TkTE8beXH(=@GZ8wv6!+|vJdZb;FQT15sZJYr z_2aVOUeozH!`P!Ye;f$zrHym#ul1VHT7q;}Zas(Bw;hMUGZ0j221oz0@J9qAoR>5Pi9YIHMLBE8Q&=)u1 z>FK#LrO{fAv>YA!h*`ahP`l#ft*JIII%Ir(j8gtja2^j%3}D0mo&OH@`5S~4dpY>; zwUAEk(9o`2e)1d8(1IEXN%e?PP28aRW;6(ZXfiI{L&X9XJE$kib`w2VxHYz@#YGTg ztsNb7L|zsP0%LU{NK|CDsd=`Y|6`%Yp}UX|&=8n$=+N1PPoTyQrh?Yq{X5+)IY&ZG zn@lF#%DG%cO!x>K@yQBy7G~1Nx0l}B*?LH6Inc~nmtd_NGV#m1XzNe@oGBU7azYR# z#tTZUFy!KzR5NW`5rkcaCi|Y4H4fgMo=UY;rlovxVRjTZO@)x zZ&|yA73^}LblMQa4`e0hA438H0&rWgR5yS|DFKj81%H32kve<+(c{Nch^>&x%PLAYJDCU}&(ckjWg%+r_T1`D*u%h}oZXVm~2da(E7xu+YyJm3pG_i@jo2egmY?Q`GqML}F-f+2VFYQKZue?AQ=Q{{Tw;hzwc zB{x;=l`vj_MCk*i$w4LANX3kWi16gWyR14mkwUQZ<&l*^o=1?cVYuW0nwNp&h0+RD zvjnBgF~A$d;G6=aYdtuo|FY7V0jCv7q*-Gl%ag3fbk0*+AyZwT5b|kSo2ObiFhI9^ErBYY33k6_i%M1! z=n8QsK7P8>8Cz~@x;iHisXg@7E5gjB+T$M<7UqvB6KH*YrW~;XHeL=O`0Hb~CHElDa!)Tunhv>BFcLXpHUhDLw)17Z5l|<$=2^C)7OT)SV9Kgs2T80o5Y-zB0q0W8%WG62N#|Kvrr@> zP!rWVtHn_0RED?H5FM_W~&4JgGL9M#h@k2 zkHduqE{9!4MrMbgp}(jn^cL0r4iAuZR_)5D`*>ff6~ov);hL!vQC zs#Nk3b+Yg7>8V~by3i;`bK}b=#byUTxkuZpv~QGPdcn3ZxZ{2G`q&bc-r0MiCeAi1 zfIN^H4tJ`7@P#5nT8HgFD(7` zi15HdQ|Fm_cs1Cdo1LWSzQyyEoLi{yVnuxf0!r^ zp=QeA#;6styEpeEgvMqUkN$FHl|`-`ZX6)nJRMS|+I2^S@0Wzd)f*LSJ7Vlt%uEIL z6qD2FcS(vf2#+#6a2VQE2fQF<=%2>iJ6H%>^I?U_oBL~_>ytH=^jdRDB~KM16v{y) zF~QkfS(aRPGwL`WpB`#rLw_6iINFJIi3#w4AQ`4#7HDwe##hL7BZvf{$j52$GnUuX zcu!AH6Q(`Xn3wd5yjx|4fPOhiNhCW&ym9-vMIQGJkikHOBN0m^2KElTq* z!RwzN5OLkEBfw_3GM3B>%QA{tbqXSvK+aT&X*mcG!l_oP>!ooFcPD^?9Y8UnU_RmG zuQdn{Fh?W}!;(oui8PGG`xnaXQa}N7a3LXpqCUKaN~MB5(Zqv@1(`h>%H8(u41x8AN&QRa`VE2;jPfw zN+%Gma>i|lbKj&762W1YBNX`B?jU@Hy?=ic(fZ6-LVG(H4xaGzFNOCHt0o?Rqo^j) zhx`@Dng_|RH?cw%$UzY*u|KM@EoH`7UI-}g?hJ&0e={u3xEw(&?plnaI~xveTxn?w zM4BZ=48pX#hD~GA^y%+oa8hZumj?L3RJJ!MDanw@Pt2JNUM>ipBoV?Zl@4pFh&~+p zl9}pTi1z+;zwS;eNc=={+-X$K(z)>CpuwJCTH>dZr+S_W80bn%dvcl<4sR_nc;B*i z;fkPTyVi9cQd1rN_&YR05@%eZ66+o?8Z0Az`GMMOd?c7#t?8vNo3U#3{Q9tkdy#_H z?P{QPntIg!qpFUBnYhwL&TGBrkf+p_WKQCbB*$*sM5cwG)-1PN`1gSg-xk7h@X3>O z#0M_RXcoRn`p+-mlRg=%Kq@%c znzHyC3t3No&DV&xRGFXXn{aAvS?IrGT~d>}mio-W2B)lX?JJ|y>9*MNFzbZ5oeN!= zH!-dnPLF#fz^-+4Uu@YGN<~g3Z$5L4faI~#kl*OtpZ5avHHM~whH+>HRKZZS_dmH4+nRRmeY4E2^)>&pEPI*_*DHL2oQNZq;bs5zC|9?w>hS`lPoqTv=9ZDz*Fl{V&qy z^INOi$N0lLq8$2sc;uLU`C`=cyW-o@`kk8tr`B%MQ<`gFqB^tq-W_jdXL#oDmo}@N zFkN4cT(;dH^(%EIK})YYF~wrsfW70MSf(eHtY@BlGewtc#@XcS3Nc|`(4mKWR9RWl3OnTZI2Hfqg5o2Q)tO+mFB;d9OM37HZ5 zg8GsbyBYa(d!5TlYV-Rj+Vq_-GxDoDI<)oM>R5YUa8KL_qGsAuo){6kQMiI1p|-5c zr=GhnAfKJHSi>!g`aag6yePIkhdg6jIyY})K3$NnH|s7JPHvj*9$IZx*7p3=jP5|T z)9|PTcWZ5jmMlemOw3ejLc_XLk#XKoR=9nz;dpm{rr=l!)AZQf<@!j)_EIeZOteu2 z`yUhsKJXXit4Qh*H%hq zlPU2Ko+_tPNq5wpH=8iNEbq=#=j_MiSXv=W^js)^Add^#rdS%QZeq~G<0W^_KGcjT%q)(U20!Mgyi^aLzN^&U(@*O zyiffJ9u6Zp}nloItuC4>; zIXtC8ZTr2lC#Nnrwe($ZvaqMY#$F{FDr;lNCkllN$Fw9bxD*m=>deeMH z<}=wO<>f6XI@oe{b1^pNowsK~rNf!y=aQLuhSCZu4V!Iy0%qNuyRlVV>FZmQ_?x6c zFBj}}quNk(hJNphP~b4g5cm60<`>j8knJV@c&F(2*h1KGRQ*8@I6XNndnMjTT(z2< z7%=BNXRoK}oqQspBj$WHbA5X|oxbjP=2n$&-~3HSn?B&W9`K5)UwMEA;=;=l&;wG8SdhOgdOXJ%P@k|C; z!vs3~HDT@W&(C&tDP$Ecfe42>lcAw`zf&{D$)%~yGQ~~|!W4}Rs{%vQ%mABaKcA0u zWX>Yb*spU8ZepT&oY9-597OCz3LAl` zKD7iDW@)oZ#`%r#6+iSoQ~)Y+3c6UEp|~*51Oxf~ay) zNyUu#r>CdGE#A+)lZvvX1b}ncOE$I}Bn|;xc~EQT*hfEA8EMWS&JGdYe;AkJ)58Cx z@4~qf+G#krJ$!wAad3+VHf80TH*XR^D{H*&P=Jt)EUF;*@AH^YzYFxneHtFEkc+j5 z2Ex{XTI46}idvWW9YJTz`-XU)v#3dW`sxWFKI~=(gCZINIOrmloe%8gonJJJYzIAnV5Z27^YBxeXvO~GW zZ$}R}S%5<|BdpXRRzJr+kjNI0uJ=nqR1HG=jE1-GLkF7P@=~)1m-aCnD(o(U0HQ~r zcF?3Ic`6Ix!sxyW8Mfl~;Jfsy9YZ)(coZ#=0Vw4^QN>qp5%FD}-CBSU?xP$272lm`%;a~=)6SuPR>yr$L z!U^NGzaa946JS39cm;mC(8k*i{bnnG?R3W9ak!usB6nmpB*zM(nuSW|0<+?7ILGHy zTVmDIpou;yprK=W4t6|sdI|@3eNCNu)M|0yC>~YrwuwCihhYNdVivqaDN&Au2iivp zmfc&+*bXvS7BAN^holu_)`;^?38*2$U@<0|f#aKrzw7uD+ZV_e!u8n;kpe2Y%{wdx z@E)O4C|Nu}ytlH8O-T~&#&GliMBIpdexmrXqI4 zCLwz!Bb)cxE`Bb&+~hpu6pP%|?5!a2icvm+_Ly#klv#k&0xw|D<;Fsmf@S+R2%c+C zdBvt$-38jcwFxG&7Zu}FpQ$rQA|V?S9GvL3>~9bNSwU3mP}!anBiWQZ#*Hms<8$f0_35Nn^HY-z*G;+{ujneh>E%> zAc(`9@i?%C#BnQG zhKGRb4*u?(hA#uG1mVJ@Br{_ zJD*2+&f$U2E0wiM2=;;)MZ(uW>@Y-_6GWGw_Y9+y56{jP#W~U<91yT*Fw1WP11+tg z(P%$#bYUOdcl=E|P0#?u*kIAfz@ae=o*T72*oauiwnLR8h(VW?mAzo2KT2?RV9-C| zaG!3>P%_?eZ|<+#K_Qq@Neb%x z{gTA$fcnn01II*hiu^WixDSEQ2C`#ffnhf8u|OSwO_ZT9S^c;~1RmmqZaB2#DNyNt zYyzN2FAjh>9XOoAQPRe=rD1({3BV z3k;}Qs5Z}OH1Fh2Yo#!meco6L*$zijduFkN3Se2#Qnw+>Yba&0L$$=>;KoZ9re-_D zf?)DKw)fnV^Ejcckmg`50B+f9TIJf5be>(#9)6?@cYT&q2!>*m`&IlMLn!&Zygs}E<5>|hh{z>z>P31%w=Vmv93*qil42rhz&O&SK@FKFPo(q)w_wTRUdHnnzTqmmJ#|us* z`SAMn>%l)wtL7`2ejNH0bAn)%aAkKHh{{63;-jPZw+NJ-gj>II3G{*Zaj5@xwkmik z-a;}q1$GUz5jtkei-;2HjoqzfRv_L8x(mtA8wp3TBgMYR6T`&jY@a^!LtajX0*}ZM zymPG>@6dY==<)77XF=XOyRj>9%Sgp@v{I{`=ah+I0ix)(ipmjE89P4Z+G2scP0@kH zj^M&UFTcdO;Nzg2G)=On+KK26{{FYBh1$YyrnYI#?~^1LW`cDM53p7xzJW}fKD}ic zP69vVGcg{$;ECPu*AyaLT*l%( z6dMgrK9dqsjuN2|$w*_ziCb6Ops;B4IH;+ra)?cSWZ;nGz*__&K?8;nq`DME3NeU- zIvq<@UAP)a|4``+-jVP&a0a4_0+H0DD201mnJ)VrxpumZ2!#72L!q2{AXh5EOQk6Q zw@Q>nA6x}(CH&3mh7C_uEYNTQfxKgjSm+eqfv1=~5-_0P>*se!+VgM(5z#Aq5UbRP zlU-5G4&TKY6%m`Rbm8?g`y|`2tYe`_WE?a5{aFf}aAb!DUfK4=Dt2K0o#|8J4o&WC z9uN+cwsEOnVV(fC!Lx`XTpo~iVjEs_8KJb+A6>)z8Y;QUkbRcoJI+`jPhnLjeYhYf#Jfg{WRs08b7|B)8{Px&>| zuRZt=2CU2>{u=?L|5H2pZ^!-*TebgA=)V(6fU^J7qw~M({C{no@9pr+qRVv8UNA>W Nip!pTclzp`{|i7~This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props }} + {{#tabs.panel name="about" }}
This is about us section
{{/tabs.panel}} - {{#tabs.panel name="contact" props=tabs.props }} + {{#tabs.panel name="contact" }}
This is the contact section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -38,14 +38,14 @@ module('Integration | Component | nucleus-tabs', function(hooks) { test('it should have only selected panel as active', async function(assert) { await render(sampleTabsTemplate); - assert.dom('.nucleus-tabs .nucleus-tabs__panel.active').exists({ count: 1 }, 'Only one active panel at a time'); - assert.dom('.nucleus-tabs .nucleus-tabs__panel.active').hasText('This is the home section'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel.is-active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs__panel.is-active').hasText('This is the home section'); }); test('it should have only selected tab list item as active', async function(assert) { await render(sampleTabsTemplate); - assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').exists({ count: 1 }, 'Only one active panel at a time'); - assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').hasText('home'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.is-active').exists({ count: 1 }, 'Only one active panel at a time'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.is-active').hasText('home'); }); test('it should attach appropriate background class for the background variant', async function(assert) { @@ -55,8 +55,8 @@ module('Integration | Component | nucleus-tabs', function(hooks) { test('it should attach appropriate line class for the line variant', async function(assert) { await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} @@ -66,48 +66,48 @@ module('Integration | Component | nucleus-tabs', function(hooks) { test('it should attach disable class to disabled tab list item', async function(assert) { await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props disabled="true" }} + {{#tabs.panel name="about" disabled="true" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} `); - assert.dom('.nucleus-tabs .nucleus-tabs__list__item.disabled').exists({ count: 1 }, 'Has disabled class when disabled prop is passed'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.is-disabled').exists({ count: 1 }, 'Has disabled class when disabled prop is passed'); }); test('it should not enable tab when tab list item is disabled', async function(assert) { await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props disabled="true" }} + {{#tabs.panel name="about" disabled="true" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); - assert.dom('.nucleus-tabs .nucleus-tabs__list__item.active').hasText('home'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.is-active)'); + assert.dom('.nucleus-tabs .nucleus-tabs__list__item.is-active').hasText('home'); }); test('it should yeilds onChange action', async function(assert) { let onChangeAction = this.spy(); this.actions.onChange = onChangeAction; await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" onChange=(action "onChange") as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" onChange=(action "onChange") as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props }} + {{#tabs.panel name="about" }}
This is the about section
{{/tabs.panel}} {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.is-active)'); assert.ok(onChangeAction.calledOnce, 'onChange action has been called.'); }); @@ -115,17 +115,17 @@ module('Integration | Component | nucleus-tabs', function(hooks) { let beforeChangeAction = this.spy(); this.actions.beforeChange = beforeChangeAction; await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" beforeChange=(action "beforeChange") as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" beforeChange=(action "beforeChange") as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props }} + {{#tabs.panel name="about" }}
This is the about section
{{/tabs.panel}} {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.is-active)'); assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); }); @@ -139,17 +139,17 @@ module('Integration | Component | nucleus-tabs', function(hooks) { }); this.actions.onChange = onChangeAction; await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" beforeChange=(action "beforeChange") onChange=(action "onChange") as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} + {{#nucleus-tabs description="site-navigation" select="home" beforeChange=(action "beforeChange") onChange=(action "onChange") as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props }} + {{#tabs.panel name="about" }}
This is the about section
{{/tabs.panel}} {{/nucleus-tabs}} `); - await click('.nucleus-tabs .nucleus-tabs__list__item:not(.active)'); + await click('.nucleus-tabs .nucleus-tabs__list__item:not(.is-active)'); assert.ok(beforeChangeAction.calledOnce, 'beforeChange action has been called.'); assert.ok(onChangeAction.calledOnce, 'beforeChange action has been called.'); assert.verifySteps(['beforeChange', 'onChange']); @@ -159,10 +159,10 @@ module('Integration | Component | nucleus-tabs', function(hooks) { await render(sampleTabsTemplate); assert.dom('.nucleus-tabs .nucleus-tabs__list').hasAttribute('role', 'tablist'); assert.dom('.nucleus-tabs .nucleus-tabs__list').hasAttribute('aria-label', 'site-navigation'); - assert.dom('.nucleus-tabs .nucleus-tabs__list button').hasAttribute('role', 'tab'); - assert.dom('.nucleus-tabs .nucleus-tabs__list button.active').hasAttribute('aria-selected', 'true'); - assert.dom('.nucleus-tabs .nucleus-tabs__list button:not(.active)').hasAttribute('aria-selected', 'false'); - assert.dom('.nucleus-tabs .nucleus-tabs__list button').hasAttribute('aria-controls'); + assert.dom('.nucleus-tabs .nucleus-tabs__list .nucleus-tabs__list__item').hasAttribute('role', 'tab'); + assert.dom('.nucleus-tabs .nucleus-tabs__list .nucleus-tabs__list__item.is-active').hasAttribute('aria-selected', 'true'); + assert.dom('.nucleus-tabs .nucleus-tabs__list .nucleus-tabs__list__item:not(.is-active)').hasAttribute('aria-selected', 'false'); + assert.dom('.nucleus-tabs .nucleus-tabs__list .nucleus-tabs__list__item').hasAttribute('aria-controls'); assert.dom('.nucleus-tabs .nucleus-tabs__panel').hasAttribute('role', 'tabpanel'); assert.dom('.nucleus-tabs .nucleus-tabs__panel').hasAttribute('aria-labelledby'); }); @@ -175,40 +175,30 @@ module('Integration | Component | nucleus-tabs', function(hooks) { }); }); - test('visual regression for default style tabs', async function(assert) { + test('visual regression for the different variants - default, background, disabled', async function(assert) { await render(hbs` - {{#nucleus-tabs description="site-navigation" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} +
Default:
+ {{#nucleus-tabs description="site-navigation" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} - `); - await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); - }); - - test('visual regression for background style tabs', async function(assert) { - await render(hbs` - {{#nucleus-tabs description="site-navigation" variant="background" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} +
With background:
+ {{#nucleus-tabs description="site-navigation" variant="background" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} - `); - await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); - }); - - test('visual regression for disabled tabs', async function(assert) { - await render(hbs` - {{#nucleus-tabs description="site-navigation" variant="background" selected="home" as |tabs|}} - {{#tabs.panel name="home" props=tabs.props }} +
With disabled:
+ {{#nucleus-tabs description="site-navigation" variant="background" select="home" as |tabs|}} + {{#tabs.panel name="home" }}
This is the home section
{{/tabs.panel}} - {{#tabs.panel name="about" props=tabs.props disabled="true" }} + {{#tabs.panel name="about" disabled="true" }}
This is the home section
{{/tabs.panel}} {{/nucleus-tabs}} `); - await backstop(assert, {scenario:{misMatchThreshold: 0.1}}); + await backstop(assert, {scenario:{misMatchThreshold: 0.001}}); }); - }); From 0606c98756e808ce7fd98457a35406e3792b0a08 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Thu, 12 Mar 2020 14:21:18 +0530 Subject: [PATCH 15/21] feat(tabs): select prop optional --- .../app/templates/components/nucleus-tabs/demo-1.hbs | 1 - .../dummy/app/templates/docs/components/nucleus-tabs.md | 7 +++---- packages/tabs/addon/components/nucleus-tabs.js | 9 +++------ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs index 44a33645..14e98cdc 100644 --- a/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs +++ b/packages/@nucleus/tests/dummy/app/templates/components/nucleus-tabs/demo-1.hbs @@ -2,7 +2,6 @@ {{#demo.example name="nucleus-tabs-2.hbs" }} {{#nucleus-tabs description="site-navigation" - select="I want apples" onChange=(action "onChange") as |tabs| }} {{#tabs.panel name="I want apples" }}
This is apples section
diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index 6f9d0cf2..c284c191 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -18,7 +18,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content customClasses="sample-tab sample-tab-simple" description="site-navigation" select="I want apples" - variant="default" as |tabs| }} + variant="line" as |tabs| }} {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} @@ -47,8 +47,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#demo.example name="nucleus-tabs-4.hbs"}} {{#nucleus-tabs description="site-navigation" - select="I want apples" - variant="default" as |tabs| }} + variant="line" as |tabs| }} {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} @@ -72,7 +71,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#nucleus-tabs description="site-navigation" select="I want apples" - variant="default" as |tabs| }} + variant="line" as |tabs| }} {{#tabs.panel name="I want apples" }}
This is apples section
{{/tabs.panel}} diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 75421534..2250652e 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -125,6 +125,7 @@ class NucleusTabs extends Component { */ @action registerPanel(tab) { + if(!get(this, 'selected') && (tab.disabled === 'false')) set(this, 'selected', tab.name); // set selected if not initially set get(this, 'tabPanels').pushObject(tab); } @@ -157,13 +158,9 @@ class NucleusTabs extends Component { const beforeChange = get(_this, 'beforeChange'); const onChange = get(_this, 'onChange'); const currentTab = get(_this, 'selected'); - if(beforeChange) { - await beforeChange.call(_this, changedTo, currentTab, event); - } + if(beforeChange) await beforeChange.call(_this, changedTo, currentTab, event); set(_this, 'selected', changedTo); - if(onChange) { - await onChange.call(_this, changedTo, currentTab, event); - } + if(onChange) await onChange.call(_this, changedTo, currentTab, event); } } From 21b017e7c5167f24144faeb31ac43da1db9944e4 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Tue, 17 Mar 2020 04:21:16 +0530 Subject: [PATCH 16/21] feat(tabs): disabled as boolean or string --- .../app/templates/docs/components/nucleus-tabs.md | 2 +- packages/tabs/addon/components/nucleus-tabs.js | 4 ++-- .../addon/components/nucleus-tabs/tab-list-item.js | 14 +++++++------- .../addon/components/nucleus-tabs/tab-panel.js | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md index c284c191..a88bb093 100644 --- a/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md +++ b/packages/@nucleus/tests/dummy/app/templates/docs/components/nucleus-tabs.md @@ -54,7 +54,7 @@ It's easy for the user to quickly distinguish which tab belongs to which content {{#tabs.panel name="I want oranges" }}
This is oranges section
{{/tabs.panel}} - {{#tabs.panel name="I want grapes" disabled="true" }} + {{#tabs.panel name="I want grapes" disabled=true }}
This is grapes section
{{/tabs.panel}} {{/nucleus-tabs}} diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 2250652e..435a397e 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -1,6 +1,6 @@ import Component from '@ember/component'; import { classNames, classNameBindings, layout as templateLayout } from '@ember-decorators/component'; -import layout from "../templates/components/nucleus-tabs"; +import layout from '../templates/components/nucleus-tabs'; import { set, get, computed, action } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { A } from '@ember/array'; @@ -44,7 +44,7 @@ class NucleusTabs extends Component { * @public */ @defaultProp - customClasses = ""; + customClasses = ''; /** * select diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 62418fec..2b32cc6f 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -95,7 +95,7 @@ class TabListItem extends Component { @computed('tabOrder', function() { let tabIndex = null; if(!get(this, 'isDisabled')) { - tabIndex = (get(this, 'tabOrder') === 0)? "0" : "-1" + tabIndex = (get(this, 'tabOrder') === 0)? '0' : '-1'; } return tabIndex; }) @@ -121,7 +121,7 @@ class TabListItem extends Component { * @public */ @computed('disabled', function() { - return (get(this, 'disabled') === 'true')? true : false; + return (get(this, 'disabled').toString() === 'true')? true : false; }) isDisabled; @@ -147,7 +147,7 @@ class TabListItem extends Component { @computed('controls', function() { return get(this, 'controls'); }) - "aria-controls"; + 'aria-controls'; /** * aria-selected @@ -159,7 +159,7 @@ class TabListItem extends Component { @computed('selected', function() { return (get(this, 'selected') === get(this, 'name')).toString(); }) - "aria-selected"; + 'aria-selected'; /** * init @@ -197,7 +197,7 @@ class TabListItem extends Component { * */ click(event) { - if(get(this, 'disabled') === 'false') { + if(get(this, 'isDisabled') === false) { get(this, 'handleActivateTab').call(this, get(this, 'name'), event); } } @@ -253,7 +253,7 @@ class TabListItem extends Component { const nextElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; if(elementInFocus && (elementInFocus.id === nextElement.id)) { return; - } else if(nextElement.getAttribute("tabindex") === null) { + } else if(nextElement.getAttribute('tabindex') === null) { get(this, '_focusNextTab').call(this, nextElement, element); } else { nextElement.focus(); @@ -273,7 +273,7 @@ class TabListItem extends Component { const previousElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; if(elementInFocus && (elementInFocus.id === previousElement.id)) { return; - } else if(previousElement.getAttribute("tabindex") === null) { + } else if(previousElement.getAttribute('tabindex') === null) { get(this, '_focusPreviousTab').call(this, previousElement, element); } else { previousElement.focus(); diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index 131f18a9..cd92b186 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -76,9 +76,9 @@ class TabPanel extends Component { @computed('tabListItems.[]', function() { const tabListItems = get(this, 'tabListItems'); const tabList = tabListItems.findBy('name', get(this, 'name')); - return (tabList)? tabList.id : ""; + return (tabList)? tabList.id : ''; }) - "aria-labelledby"; + 'aria-labelledby'; /** * init From d358915777b577d8bac4d6049407ee7f2b7f0735 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 18 Mar 2020 16:09:31 +0530 Subject: [PATCH 17/21] fix(tabs): focus styling not appearing on click --- .../components/nucleus-tabs/tab-list-item.js | 38 ++++++++++++++++++- .../styles/components/_nucleus-tabs.scss | 8 ++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 2b32cc6f..8441ea75 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -1,7 +1,7 @@ import Component from '@ember/component'; import { classNames, attributeBindings, classNameBindings, tagName, layout as templateLayout } from '@ember-decorators/component'; import layout from '../../templates/components/nucleus-tabs/tab-list-item'; -import { get, computed } from '@ember/object'; +import { get, set, computed } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @@ -19,6 +19,7 @@ import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @templateLayout(layout) @classNames('nucleus-tabs__list__item') @classNameBindings('isActive:is-active') +@classNameBindings('isPressed:is-pressed') @classNameBindings('isDisabled:is-disabled') @attributeBindings('tabindex') @attributeBindings('role') @@ -85,6 +86,17 @@ class TabListItem extends Component { */ role = 'tab'; + /** + * isPressed + * + * @field isPressed + * @description to solve focus on click styling + * @type boolean + * @default false + * @public + */ + isPressed = false; + /** * tabindex * @@ -188,6 +200,30 @@ class TabListItem extends Component { }); } + /** + * mouseDown + * + * @method mouseDown + * @description event handler : We are negating the style applied on click-focus + * @public + * + */ + mouseDown() { + if(get(this, 'isDisabled') === false) set(this, 'isPressed', true); + } + + /** + * focusOut + * + * @method focusOut + * @description event handler : Removing style that was applied during press to negate focus + * @public + * + */ + focusOut() { + if(get(this, 'isPressed') === true) set(this, 'isPressed', false); + } + /** * click * diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 4223f68a..2a4d630d 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -78,6 +78,14 @@ } } + &.is-pressed { + &::after { + border: 0; + border-radius: 0; + border-bottom: 2px solid $color-azure-800; + } + } + &.is-active { color: $color-azure-800; -webkit-text-fill-color: $color-azure-800; From b3867bbad8e292d72abb50326e97f6450302b437 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 18 Mar 2020 17:31:20 +0530 Subject: [PATCH 18/21] fix(tabs): removed vendor prefixes --- packages/tabs/addon/styles/components/_nucleus-tabs.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index 2a4d630d..be9283c9 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -18,7 +18,6 @@ } &__item { - all: unset; flex-shrink: 0; position: relative; text-align: center; @@ -27,7 +26,6 @@ margin: 0 4px; font-size: 14px; color: $color-smoke-700; - -webkit-text-fill-color: $color-smoke-700; &:first-child { margin-left: 0; @@ -88,7 +86,6 @@ &.is-active { color: $color-azure-800; - -webkit-text-fill-color: $color-azure-800; font-weight: 500; &::after { @@ -98,7 +95,6 @@ &.is-disabled { color: $color-smoke-300; - -webkit-text-fill-color: $color-smoke-300; &:hover { cursor: default; From 678ebfa57a28b5ae1b0613c6a3335661157f0738 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Wed, 18 Mar 2020 18:10:20 +0530 Subject: [PATCH 19/21] fix(tabs): pressed and active hover state styling --- packages/tabs/addon/styles/components/_nucleus-tabs.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/tabs/addon/styles/components/_nucleus-tabs.scss b/packages/tabs/addon/styles/components/_nucleus-tabs.scss index be9283c9..4387ad74 100644 --- a/packages/tabs/addon/styles/components/_nucleus-tabs.scss +++ b/packages/tabs/addon/styles/components/_nucleus-tabs.scss @@ -80,6 +80,14 @@ &::after { border: 0; border-radius: 0; + border-bottom: 0; + } + + &:hover::after { + border-bottom: 2px solid $color-smoke-100; + } + + &.is-active::after { border-bottom: 2px solid $color-azure-800; } } From c4d76765c37d3be2fea977131f0009f4cf457895 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Thu, 19 Mar 2020 13:05:04 +0530 Subject: [PATCH 20/21] refactor(tabs): Combined attribute bindings, class bindings. Single line if statements wrapped --- .../tabs/addon/components/nucleus-tabs.js | 24 ++++++++++--------- .../components/nucleus-tabs/tab-list-item.js | 19 +++++++-------- .../components/nucleus-tabs/tab-panel.js | 16 +------------ 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs.js b/packages/tabs/addon/components/nucleus-tabs.js index 435a397e..3e439137 100644 --- a/packages/tabs/addon/components/nucleus-tabs.js +++ b/packages/tabs/addon/components/nucleus-tabs.js @@ -1,7 +1,7 @@ import Component from '@ember/component'; import { classNames, classNameBindings, layout as templateLayout } from '@ember-decorators/component'; import layout from '../templates/components/nucleus-tabs'; -import { set, get, computed, action } from '@ember/object'; +import { set, get, getProperties, computed, action } from '@ember/object'; import defaultProp from '@freshworks/core/utils/default-decorator'; import { A } from '@ember/array'; import { oneWay }from '@ember/object/computed'; @@ -18,8 +18,7 @@ import { oneWay }from '@ember/object/computed'; */ @templateLayout(layout) @classNames('nucleus-tabs') -@classNameBindings('variantClass') -@classNameBindings('customClasses') +@classNameBindings('variantClass', 'customClasses') class NucleusTabs extends Component { /** * description @@ -125,7 +124,9 @@ class NucleusTabs extends Component { */ @action registerPanel(tab) { - if(!get(this, 'selected') && (tab.disabled === 'false')) set(this, 'selected', tab.name); // set selected if not initially set + if(!get(this, 'selected') && (tab.disabled === 'false')) { + set(this, 'selected', tab.name); // set selected if not initially set + } get(this, 'tabPanels').pushObject(tab); } @@ -154,13 +155,14 @@ class NucleusTabs extends Component { */ @action async activateTab(changedTo, event) { - const _this = this; - const beforeChange = get(_this, 'beforeChange'); - const onChange = get(_this, 'onChange'); - const currentTab = get(_this, 'selected'); - if(beforeChange) await beforeChange.call(_this, changedTo, currentTab, event); - set(_this, 'selected', changedTo); - if(onChange) await onChange.call(_this, changedTo, currentTab, event); + const { beforeChange, onChange, currentTab } = getProperties(this, 'beforeChange', 'onChange', 'currentTab'); + if(beforeChange) { + await beforeChange.call(this, changedTo, currentTab, event); + } + set(this, 'selected', changedTo); + if(onChange) { + await onChange.call(this, changedTo, currentTab, event); + } } } diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 8441ea75..461575df 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -18,15 +18,8 @@ import { TABS_KEY_CODE } from '../../constants/nucleus-tabs' @tagName('div') @templateLayout(layout) @classNames('nucleus-tabs__list__item') -@classNameBindings('isActive:is-active') -@classNameBindings('isPressed:is-pressed') -@classNameBindings('isDisabled:is-disabled') -@attributeBindings('tabindex') -@attributeBindings('role') -@attributeBindings('aria-controls') -@attributeBindings('aria-selected') -@attributeBindings('isDisabled:disabled') -@attributeBindings('title') +@classNameBindings('isActive:is-active', 'isDisabled:is-disabled', 'isPressed:is-pressed') +@attributeBindings('isDisabled:disabled', 'tabindex', 'title', 'role', 'aria-controls', 'aria-selected') class TabListItem extends Component { /** @@ -209,7 +202,9 @@ class TabListItem extends Component { * */ mouseDown() { - if(get(this, 'isDisabled') === false) set(this, 'isPressed', true); + if(get(this, 'isDisabled') === false) { + set(this, 'isPressed', true); + } } /** @@ -221,7 +216,9 @@ class TabListItem extends Component { * */ focusOut() { - if(get(this, 'isPressed') === true) set(this, 'isPressed', false); + if(get(this, 'isPressed') === true) { + set(this, 'isPressed', false); + } } /** diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js index cd92b186..9303c6c5 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-panel.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-panel.js @@ -18,9 +18,7 @@ import defaultProp from '@freshworks/core/utils/default-decorator'; @templateLayout(layout) @classNames('nucleus-tabs__panel') @classNameBindings('isActive:is-active') -@attributeBindings('tabindex') -@attributeBindings('role') -@attributeBindings('aria-labelledby') +@attributeBindings('tabindex', 'role', 'aria-labelledby') class TabPanel extends Component { /** @@ -80,18 +78,6 @@ class TabPanel extends Component { }) 'aria-labelledby'; - /** - * init - * - * @method init - * @description lifecycle event - * @public - * - */ - init() { - super.init(...arguments); - } - /** * didInsertElement * From f9086e22b086f4b1a376e2db404c8043f959ef36 Mon Sep 17 00:00:00 2001 From: Vignesh-Manogar-E3433 Date: Thu, 19 Mar 2020 14:49:12 +0530 Subject: [PATCH 21/21] refactor(tabs): changeFocusTab function --- .../components/nucleus-tabs/tab-list-item.js | 58 ++++++------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js index 461575df..21966c89 100644 --- a/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js +++ b/packages/tabs/addon/components/nucleus-tabs/tab-list-item.js @@ -166,18 +166,6 @@ class TabListItem extends Component { }) 'aria-selected'; - /** - * init - * - * @method init - * @description lifecycle event - * @public - * - */ - init() { - super.init(...arguments); - } - /** * didInsertElement * @@ -263,10 +251,10 @@ class TabListItem extends Component { firstElement.focus(); break; case keyCode.LEFT: - get(this, '_focusPreviousTab').call(this, targetElement); + get(this, '_changeTabFocus').call(this, 'previous', targetElement); break; case keyCode.RIGHT: - get(this, '_focusNextTab').call(this, targetElement); + get(this, '_changeTabFocus').call(this, 'next', targetElement); break; default: break; @@ -274,42 +262,30 @@ class TabListItem extends Component { } /** - * _focusNextTab - * When last item, focus must circle back to previous item. + * _changeTabFocus * - * @method _focusNextTab - * @description focus the next tab that is not disabled + * @method _changeTabFocus + * @description Change focus based on direction + * @param {string} direction takes either 'next' or 'previous' + * @param {Object} element + * @param {Object} [elementInFocus] * @private * */ - _focusNextTab(element, elementInFocus) { - const nextElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; - if(elementInFocus && (elementInFocus.id === nextElement.id)) { - return; - } else if(nextElement.getAttribute('tabindex') === null) { - get(this, '_focusNextTab').call(this, nextElement, element); + _changeTabFocus(direction, element, elementInFocus) { + let adjacentElement; + if(direction === 'previous') { + adjacentElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; } else { - nextElement.focus(); + adjacentElement = (element.nextElementSibling)? element.nextElementSibling : element.parentElement.firstElementChild; } - } - /** - * _focusPreviousTab - * When first item, focus must go back to last item. - * - * @method _focusPreviousTab - * @description focus the previous tab that is not disabled - * @private - * - */ - _focusPreviousTab(element, elementInFocus) { - const previousElement = (element.previousElementSibling)? element.previousElementSibling : element.parentElement.lastElementChild; - if(elementInFocus && (elementInFocus.id === previousElement.id)) { + if(elementInFocus && (elementInFocus.id === adjacentElement.id)) { return; - } else if(previousElement.getAttribute('tabindex') === null) { - get(this, '_focusPreviousTab').call(this, previousElement, element); + } else if(adjacentElement.getAttribute('tabindex') === null) { + get(this, '_changeTabFocus').call(this, direction, adjacentElement, element); } else { - previousElement.focus(); + adjacentElement.focus(); } } }