Skip to content

Commit

Permalink
refactor(tabs): remove pfe package dependency (#1398)
Browse files Browse the repository at this point in the history
* refactor(tabs): copy base classes from pfe to remove package dependency

* chore(tabs): add changeset

* fix(tabs): lint

* fix(tab): remove unused property import

* fix(tabs): lint

* fix(tabs): lint

* docs: update changeset

* fix: fold basetab code into rhtab files

* style(tabs): lint css

---------

Co-authored-by: Benny Powers <[email protected]>
Co-authored-by: Benny Powers - עם ישראל חי! <[email protected]>
Co-authored-by: Mark Caron <[email protected]>
  • Loading branch information
4 people authored Jan 8, 2024
1 parent 8c5559c commit b123092
Show file tree
Hide file tree
Showing 7 changed files with 541 additions and 75 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-shirts-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rhds/elements": patch
---

`<rh-tabs>`: removed dependency on `@patternfly/elements` package.
3 changes: 2 additions & 1 deletion elements/rh-tabs/rh-tab-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
padding-inline: var(--_panels-overflow-padding, var(--rh-space-4xl, 64px));
}

:host([hidden]) {
:host([hidden]),
[hidden] {
display: none !important;
}

Expand Down
32 changes: 26 additions & 6 deletions elements/rh-tabs/rh-tab-panel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { html } from 'lit';
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';
import { property } from 'lit/decorators/property.js';

import { classMap } from 'lit/directives/class-map.js';

import { BaseTabPanel } from '@patternfly/elements/pf-tabs/BaseTabPanel.js';
import { getRandomId } from '@patternfly/pfe-core/functions/random.js';

import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js';

Expand All @@ -17,7 +15,7 @@ import styles from './rh-tab-panel.css';
*
*/
@customElement('rh-tab-panel')
export class RhTabPanel extends BaseTabPanel {
export class RhTabPanel extends LitElement {
static readonly version = '{{version}}';

static readonly styles = [styles];
Expand All @@ -27,10 +25,32 @@ export class RhTabPanel extends BaseTabPanel {
*/
@colorContextConsumer() private on?: ColorTheme;

#internals = this.attachInternals();

connectedCallback() {
super.connectedCallback();
this.id ||= getRandomId('pf-tab-panel');
this.hidden ??= true;
this.#internals.role = 'tabpanel';

/*
To make it easy for screen reader users to navigate from a tab
to the beginning of content in the active tabpanel, the tabpanel
element has tabindex="0" to include the panel in the page Tab sequence.
It is recommended that all tabpanel elements in a tab set are focusable
if there are any panels in the set that contain content where the first
element in the panel is not focusable.
https://www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-automatic
*/
this.tabIndex = 0;
}

render() {
const { on = '' } = this;
return html`
<div id="rhds-container" class="${classMap({ [on]: !!on })}">${super.render()}</div>
<div id="rhds-container" class="${classMap({ [on]: !!on })}">
<slot></slot>
</div>
`;
}
}
Expand Down
112 changes: 81 additions & 31 deletions elements/rh-tabs/rh-tab.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,84 @@
:host {
display: flex;
flex: none;
}

:host([vertical]) [part="text"] {
max-width: 100%;
overflow-wrap: break-word;
}

[hidden] {
display: none !important;
}

slot[name="icon"] {
display: block;
}

button {
margin: 0;
font-family: inherit;
font-size: 100%;
border: 0;
position: relative;
display: flex;
flex: 1;
text-decoration: none;
cursor: pointer;
align-items: center;
background-color: inherit;
color: var(--_button-text-color);
padding-block-start: var(--rh-tabs-link-padding-block-start, var(--rh-space-lg, 16px));
padding-inline-start: var(--rh-tabs-link-padding-inline-start, var(--rh-space-2xl, 32px));
padding-block-end: var(--rh-tabs-link-padding-block-end, var(--rh-space-lg, 16px));
padding-inline-end: var(--rh-tabs-link-padding-inline-end, var(--rh-space-2xl, 32px));
max-width: 200px;
max-height: 100%;
}

button:before,
button:after {
position: absolute;
inset: 0;
content: "";
border-style: solid;
padding: 0;
margin: 0;
background-color: transparent;
}

button:before {
pointer-events: none;
border: 0 solid transparent;
}

button:focus-visible {
outline: var(--rh-border-width-md, 2px) solid var(--_button-focus-outline-color);
outline-offset: -8px;
border-radius: 10px;
}

button:after {
background-color: transparent;
border-inline: 0 solid transparent;
border-block-start: var(--rh-border-width-lg, 3px) solid transparent;
border-block-end: var(--rh-border-width-sm, 1px) solid transparent;
}

:host([fill]) button {
flex-basis: 100%;
justify-content: center;
}

:host(:disabled) button {
pointer-events: none;
}

:host([aria-disabled="true"]) button {
cursor: default;
}

#rhds-container {
display: flex;
width: 100%;
Expand Down Expand Up @@ -72,37 +153,6 @@
);
}

/* Button Resets */
button {
display: flex;
align-items: center;
background-color: inherit;
color: var(--_button-text-color);
padding-block-start: var(--rh-tabs-link-padding-block-start, var(--rh-space-lg, 16px));
padding-inline-start: var(--rh-tabs-link-padding-inline-start, var(--rh-space-2xl, 32px));
padding-block-end: var(--rh-tabs-link-padding-block-end, var(--rh-space-lg, 16px));
padding-inline-end: var(--rh-tabs-link-padding-inline-end, var(--rh-space-2xl, 32px));
max-width: 200px;
max-height: 100%;
}

button:focus-visible {
outline: var(--rh-border-width-md, 2px) solid var(--_button-focus-outline-color);
outline-offset: -8px;
border-radius: 10px;
}

button:before {
border: 0 solid transparent;
}

button:after {
background-color: transparent;
border-inline: 0 solid transparent;
border-block-start: var(--rh-border-width-lg, 3px) solid transparent;
border-block-end: var(--rh-border-width-sm, 1px) solid transparent;
}

:host([vertical]) button {
text-align: start;
}
Expand Down
99 changes: 88 additions & 11 deletions elements/rh-tabs/rh-tab.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { html } from 'lit';
import type { PropertyValues } from 'lit';

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';
import { property } from 'lit/decorators/property.js';

import { queryAssignedElements } from 'lit/decorators/query-assigned-elements.js';
import { query } from 'lit/decorators/query.js';
import { classMap } from 'lit/directives/class-map.js';

import { observed } from '@patternfly/pfe-core/decorators.js';

import { BaseTab } from '@patternfly/elements/pf-tabs/BaseTab.js';

import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js';

import { getRandomId } from '@patternfly/pfe-core/functions/random.js';

import styles from './rh-tab.css';

export class TabExpandEvent extends Event {
constructor(
public active: boolean,
public tab: RhTab,
) {
super('expand', { bubbles: true, cancelable: true });
}
}

/**
* The tab button for use within a rh-tabs element, must be paired with a rh-tab-panel.
*
Expand All @@ -32,28 +44,93 @@ import styles from './rh-tab.css';
* @fires { TabExpandEvent } expand - when a tab expands
*/
@customElement('rh-tab')
export class RhTab extends BaseTab {
export class RhTab extends LitElement {
static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };

static readonly version = '{{version}}';

static readonly styles = [...BaseTab.styles, styles];
static readonly styles = [styles];

/** `active` should be observed, and true when the tab is selected */
@observed
@property({ reflect: true, type: Boolean }) active = false;

/** `disabled` should be observed, and true when the tab is disabled */
@observed
@property({ reflect: true, type: Boolean }) disabled = false;

/**
* Sets color theme based on parent context
*/
@colorContextConsumer() private on?: ColorTheme;

@observed
@property({ reflect: true, type: Boolean }) active = false;
@queryAssignedElements({ slot: 'icon', flatten: true })
private icons!: Array<HTMLElement>;

@observed
@property({ reflect: true, type: Boolean }) disabled = false;
@query('button') private button!: HTMLButtonElement;

#internals = this.attachInternals();

connectedCallback() {
super.connectedCallback();
this.id ||= getRandomId(this.localName);
this.addEventListener('click', this.#clickHandler);
this.#internals.role = 'tab';
}

render() {
const { on = '' } = this;
return html`
<div id="rhds-container" class="${classMap({ [on]: !!on })}">${super.render()}</div>
<div id="rhds-container" class="${classMap({ [on]: !!on })}">
<button part="button" ?disabled="${this.disabled}">
<slot name="icon"
part="icon"
?hidden="${!this.icons.length}"
@slotchange="${() => this.requestUpdate()}"></slot>
<slot part="text"></slot>
</button>
</div>
`;
}

updated(changed: PropertyValues<this>) {
this.#internals.ariaSelected = String(this.ariaSelected);
if (changed.has('active')) {
this.#activeChanged();
}
if (changed.has('disabled')) {
this.#disabledChanged();
}
}

#clickHandler() {
if (!this.disabled && this.#internals.ariaDisabled !== 'true' && this.ariaDisabled !== 'true') {
this.active = true;
this.focus(); // safari fix
}
}

#activeChanged() {
if (this.active && !this.disabled) {
this.#internals.ariaSelected = 'true';
} else {
this.#internals.ariaSelected = 'false';
}
this.dispatchEvent(new TabExpandEvent(this.active, this));
}

/**
* if a tab is disabled, then it is also aria-disabled
* if a tab is removed from disabled its not necessarily
* not still aria-disabled so we don't remove the aria-disabled
*/
#disabledChanged() {
this.#internals.ariaDisabled = String(!!this.disabled);
}

focus() {
this.button.focus();
}
}

declare global {
Expand Down
Loading

0 comments on commit b123092

Please sign in to comment.