Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ef659f4
Add settings button to table
sdvg Apr 22, 2025
1b7c1e7
Introduce kol-popover-button-wc
sdvg Apr 22, 2025
06c5fc1
Organize styling
sdvg Apr 22, 2025
e26152c
Add table settings component for configurable column layouts
sdvg Apr 22, 2025
8ad6f85
Fix mixins and button styles
sdvg Apr 22, 2025
c833365
Provide translations
sdvg Apr 22, 2025
3c905db
Tweak table settings layout
sdvg Apr 23, 2025
65c4f4e
Consider scrollable tables
sdvg Apr 23, 2025
6521039
Add focus style
sdvg Apr 23, 2025
fc47d53
Introduce state and add sort feature
sdvg Apr 23, 2025
7f23496
Implement method hidePopover for kol-popover-button
sdvg Apr 23, 2025
ac40fe6
Implement settings cancel button
sdvg Apr 23, 2025
93022b4
Fix duplicate key in sample
sdvg Apr 24, 2025
868b7ba
Derive column settings from headers
sdvg Apr 24, 2025
ef09649
Handle settings events
sdvg Apr 24, 2025
5a421a6
Handle visibility and width changes
sdvg Apr 24, 2025
060ce14
Apply column width settings
sdvg Apr 24, 2025
b06c9dc
Implement column order
sdvg Apr 24, 2025
d990fa3
Update Jest snapshots
sdvg Apr 24, 2025
a2bb475
Update all snapshots$
sdvg Apr 24, 2025
20500a5
Manage props and state
sdvg Apr 29, 2025
aa66e33
Set column width minimum value to 0
sdvg Apr 29, 2025
f2851a1
Validate minimum number of visible columns
sdvg Apr 29, 2025
0e03f78
Add sample with predefined settings
sdvg Apr 29, 2025
00b36f6
Wrap inputs in form and fix occasional exception when moving columns
sdvg Apr 30, 2025
132b89a
Calculate table width based on settings
sdvg Apr 30, 2025
62ecd44
Fix Jest snapshots
sdvg Apr 30, 2025
819a75c
Add E2E tests
sdvg May 1, 2025
8af83e2
Merge branch 'develop' into 1834-table-settings
sdvg May 5, 2025
0dca2f2
Rename settings change event for better consistency and add test
sdvg May 5, 2025
07451b5
Merge remote-tracking branch 'origin/1834-table-settings' into 1834-t…
sdvg May 5, 2025
11c9188
Fix table settings button overlapping with table borders in default t…
sdvg May 6, 2025
c69dadb
Merge branch 'develop' of github.com:public-ui/kolibri into 1834-tabl…
sdvg May 6, 2025
7009776
Update all snapshots$
sdvg May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@use 'mixins' as *;

@mixin kol-popover-button-styles {
@layer kol-component {
.kol-popover-button {
&__button {
display: inline-block; // critical for floating UI to work correctly
}

&__popover {
border: 1px solid #000;
margin: 0;
padding: 0;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@use './mixins' as *;

@mixin kol-table-settings-styles {
@layer kol-component {
.kol-table-settings {
background: #fff;
position: absolute;
right: 0;
top: 0;
z-index: 1;

&__columns-container {
margin-top: rem(16);
max-height: rem(200);
overflow: auto;
}

&__columns {
align-items: center;
display: grid;
gap: rem(8);
grid-auto-rows: min-content;
grid-template-columns: min-content 1fr rem(100) auto auto;
overflow: hidden;
}

&__column {
display: contents;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
@use '../@shared/mixins' as *;
@use '../host-display-block' as *;
@use '../tooltip/style.scss' as *;
@use '../tooltip/style' as *;
@use './kol-table-settings-mixin' as *;
@use './mixins' as *;

@mixin kol-table-stateless-styles {
@include kol-table-settings-styles;

@layer kol-component {
.kol-table {
display: block;
font-size: rem(16);

max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
position: relative;

&__scroll-container {
overflow-x: auto;
overflow-y: hidden;
}

&__table {
width: 100%;
}

&__caption {
text-align: start;
min-height: var(--a11y-min-size); // align with configuration button
padding-right: var(--a11y-min-size);
}

&__focus-element {
Expand Down
257 changes: 257 additions & 0 deletions packages/components/src/components/popover-button/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import type { JSX } from '@stencil/core';
import { Component, h, Method, Prop, State, Watch } from '@stencil/core';
import { KolButtonWcTag } from '../../core/component-names';
import { alignFloatingElements } from '../../utils/align-floating-elements';
import type { PopoverButtonProps, PopoverButtonStates } from '../../schema/components/popover-button';
import type {
AccessKeyPropType,
AlternativeButtonLinkRolePropType,
AriaDescriptionPropType,
ButtonCallbacksPropType,
ButtonTypePropType,
ButtonVariantPropType,
CustomClassPropType,
IconsPropType,
LabelWithExpertSlotPropType,
PopoverAlignPropType,
ShortKeyPropType,
StencilUnknown,
Stringified,
SyncValueBySelectorPropType,
TooltipAlignPropType,
} from '../../schema';
import { validatePopoverAlign } from '../../schema';

/**
* @slot - The popover content.
*/
@Component({
tag: 'kol-popover-button-wc',
shadow: false,
})
// class implementing PopoverButtonProps and not API because we don't want to repeat the entire state and validation for button props
export class KolPopoverButton implements PopoverButtonProps {
private refButton?: HTMLKolButtonWcElement;
private refPopover?: HTMLDivElement;

@State() public state: PopoverButtonStates = {
_label: '',
_popoverAlign: 'bottom',
};
@State() private justClosed = false;

/**
* Hides the popover programmatically by calling the native hidePopover method.
*/
@Method()
// eslint-disable-next-line @typescript-eslint/require-await
public async hidePopover() {
void this.refPopover?.hidePopover();
}

/* Regarding type issue see https://github.com/microsoft/TypeScript/issues/54864 */
private handleBeforeToggle(event: Event) {
if ((event as ToggleEvent).newState === 'closed') {
this.justClosed = true;

setTimeout(() => {
// Reset the flag after the event loop tick.
this.justClosed = false;
}, 10); // timeout of 0 should be sufficient but doesn't work in Safari Mobile (needs further investigation).
} else if (this.refPopover) {
/**
* Avoid "flicker" by hiding the element until the position is set in the `toggle` event handler. `alignFloatingElements` is responsible for setting the visibility back to 'visible'.
*/
this.refPopover.style.visibility = 'hidden';
}
}

private handleToggle(event: Event) {
if ((event as ToggleEvent).newState === 'open' && this.refPopover && this.refButton) {
void alignFloatingElements({
align: this.state._popoverAlign,
floatingElement: this.refPopover,
referenceElement: this.refButton,
});
}
}

private handleButtonClick() {
// If the popover was just closed by native behavior, do nothing (and let it stay closed).
if (!this.justClosed) {
this.refPopover?.togglePopover();
}
}

public componentDidRender() {
this.refPopover?.addEventListener('toggle', this.handleToggle.bind(this));
this.refPopover?.addEventListener('beforetoggle', this.handleBeforeToggle.bind(this));
}

public disconnectedCallback() {
this.refPopover?.removeEventListener('toggle', this.handleToggle.bind(this));
this.refPopover?.removeEventListener('beforetoggle', this.handleBeforeToggle.bind(this));
}

public render(): JSX.Element {
return (
<div class="kol-popover-button">
<KolButtonWcTag
_accessKey={this._accessKey}
_ariaControls={this._ariaControls}
_ariaDescription={this._ariaDescription}
_ariaExpanded={this._ariaExpanded}
_ariaSelected={this._ariaSelected}
_customClass={this._customClass}
_disabled={this._disabled}
_hideLabel={this._hideLabel}
_icons={this._icons}
_id={this._id}
_label={this._label}
_name={this._name}
_on={this._on}
_role={this._role}
_shortKey={this._shortKey}
_syncValueBySelector={this._syncValueBySelector}
_tabIndex={this._tabIndex}
_tooltipAlign={this._tooltipAlign}
_type={this._type}
_value={this._value}
_variant={this._variant}
data-testid="popover-button"
class="kol-popover-button__button"
ref={(element) => (this.refButton = element)}
onClick={this.handleButtonClick.bind(this)}
>
<slot name="expert" slot="expert"></slot>
</KolButtonWcTag>

<div ref={(element) => (this.refPopover = element)} data-testid="popover-content" popover="auto" id="popover" class="kol-popover-button__popover">
<slot />
</div>
</div>
);
}

/**
* Defines which key combination can be used to trigger or focus the interactive element of the component.
*/
@Prop() public _accessKey?: AccessKeyPropType;

/**
* Defines which elements are controlled by this component. (https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-controls)
*/
@Prop() public _ariaControls?: string;

/**
* Defines the value for the aria-description attribute.
*/
@Prop() public _ariaDescription?: AriaDescriptionPropType;

/**
* Defines whether the interactive element of the component expanded something. (https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded)
*/
@Prop() public _ariaExpanded?: boolean;

/**
* Defines whether the interactive element of the component is selected (e.g. role=tab). (https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-selected)
*/
@Prop() public _ariaSelected?: boolean;

/**
* Defines the custom class attribute if _variant="custom" is set.
*/
@Prop() public _customClass?: CustomClassPropType;

/**
* Makes the element not focusable and ignore all events.
*/
@Prop() public _disabled?: boolean = false;

/**
* Hides the caption by default and displays the caption text with a tooltip when the
* interactive element is focused or the mouse is over it.
* @TODO: Change type back to `HideLabelPropType` after Stencil#4663 has been resolved.
*/
@Prop() public _hideLabel?: boolean = false;

/**
* Defines the icon classnames (e.g. `_icons="fa-solid fa-user"`).
*/
@Prop() public _icons?: IconsPropType;

/**
* Defines the internal ID of the primary component element.
*/
@Prop() public _id?: string;

/**
* Defines the visible or semantic label of the component (e.g. aria-label, label, headline, caption, summary, etc.). Set to `false` to enable the expert slot.
*/
@Prop() public _label!: LabelWithExpertSlotPropType;

/**
* Defines the technical name of an input field.
*/
@Prop() public _name?: string;

/**
* Defines the callback functions for button events.
*/
@Prop() public _on?: ButtonCallbacksPropType<StencilUnknown>;

/**
* Defines where to show the Popover preferably: top, right, bottom or left.
*/
@Prop() public _popoverAlign?: PopoverAlignPropType = 'bottom';

/**
* Defines the role of the components primary element.
*/
@Prop() public _role?: AlternativeButtonLinkRolePropType;

/**
* Adds a visual short key hint to the component.
*/
@Prop() public _shortKey?: ShortKeyPropType;

/**
* Selector for synchronizing the value with another input element.
* @internal
*/
@Prop() public _syncValueBySelector?: SyncValueBySelectorPropType;

/**
* Defines which tab-index the primary element of the component has. (https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex)
*/
@Prop() public _tabIndex?: number;

/**
* Defines where to show the Tooltip preferably: top, right, bottom or left.
*/
@Prop() public _tooltipAlign?: TooltipAlignPropType = 'top';

/**
* Defines either the type of the component or of the components interactive element.
*/
@Prop() public _type?: ButtonTypePropType = 'button';

/**
* Defines the value that the button emits on click.
*/
@Prop() public _value?: Stringified<StencilUnknown>;

/**
* Defines which variant should be used for presentation.
*/
@Prop() public _variant?: ButtonVariantPropType = 'normal';

@Watch('_popoverAlign')
public validatePopoverAlign(value?: PopoverAlignPropType): void {
validatePopoverAlign(this, value);
}

public componentWillLoad() {
this.validatePopoverAlign(this._popoverAlign);
}
}
Loading
Loading