Skip to content

Comments

feat(checkbox): add indeterminate state for partial selection#1112

Open
Copilot wants to merge 4 commits intomainfrom
copilot/add-intermediate-checkbox-state
Open

feat(checkbox): add indeterminate state for partial selection#1112
Copilot wants to merge 4 commits intomainfrom
copilot/add-intermediate-checkbox-state

Conversation

Copy link
Contributor

Copilot AI commented Nov 26, 2025

  • Analyze current checkbox implementation
  • Review Figma design specs for indeterminate state
  • Verify "less" icon is available in blip-tokens
  • Add indeterminate property to bds-checkbox component
  • Update checkbox styles for indeterminate state (enabled and disabled)
  • Update render logic to show minus icon when indeterminate
  • Update onClick behavior (indeterminate + click = checked)
  • Update toggle method to handle indeterminate
  • Add unit tests for indeterminate state
  • Add e2e tests for indeterminate state
  • Update Storybook stories to use DS components (bds-grid, bds-typo)
  • Add backwards compatibility tests
  • Build and verify changes

Changes

Component (checkbox.tsx)

  • Added new indeterminate boolean property (mutable, reflected)
  • When indeterminate, displays the "less" icon instead of checkmark
  • Clicking when indeterminate transitions to checked state
  • Default behavior (checked/unchecked) preserved when indeterminate is omitted or false

Styles (checkbox.scss)

  • Added .checkbox--indeterminate and .checkbox--indeterminate-disabled states

Storybook (checkbox.stories.jsx)

  • Updated to use Design System components (bds-grid, bds-typo) instead of raw HTML
  • Added AllStates story showing all 6 states
  • Added IndeterminateExample with interactive parent-child demo

Tests

  • Added 36 unit tests (including 3 new backwards compatibility tests)
  • Added 10 e2e tests

Screenshots

All States

checkbox-all-states

Indeterminate Example

checkbox-indeterminate-example

Original prompt

This section details on the original issue you should resolve

<issue_title>bds-checkbox add intermediate state to the checkbox</issue_title>
<issue_description>Essa funcionalidade está relacionada a algum problema? Descreva.
O componente de checkbox como se encontra hoje possui apenas dois estados: selecionado e não selecionado. É necessário adicionar um estado intermediário. Este estado é utilizado quando ocorre a “seleção em lote”, ou seja, quando alguns itens vinculados ao checkbox principal estiverem selecionados, porém não todos, este assumirá o estado intermediário.


Descreva a solução que você gostaria
Como o Componente Funciona Nesse Estado

O Checkbox Principal entra no estado Intermediário quando:

  • Existem Sub-itens: O checkbox é um controle pai de uma lista de checkboxes filhos
  • Seleção Parcial: Alguns (pelo menos um), mas não todos, dos sub-itens/checkboxes filhos foram selecionados pelo usuário.

Representação Visual
Em vez do ícone de ✓ (checado) ou do quadrado vazio (não checado), o checkbox Principal exibe uma marca diferente, geralmente um traço/hífen ou um quadrado preenchido.

Comportamento de Interação (Próximo Clique)
O comportamento principal a ser definido é o que acontece quando o usuário clica no checkbox Principal enquanto ele está no estado Intermediário:

  • Ação Padrão: O clique deve forçar a seleção de todos os sub-itens.
  • Transição de Estado: O checkbox Principal deve, então, passar do estado Intermediário para o estado Checado (checked).

Descreva alternativas que você considerou
Os itens vinculados ao checkBox Principal não precisam ser necessariamente outros checkBoxes, poderão ser utilizados outros componentes ou estado que representem a seleção de elementos. Abaixo pode-se observar alguns exemplos de seleção em lote com outros componentes e elementos.


Componente do Design System
Qual componente do design system está relacionado a essa solicitação?
bds-checkbox

Link(s) do Figma
Forneça o(s) link(s) do arquivo Figma relevante(s) para essa solicitação.
https://www.figma.com/design/2bBnJVKkG09JPWCFBIcD1y/-DS--Components?node-id=12310-328708&t=O0vPq23PDZb2szoq-0


Isso é uma alteração em um componente existente ou criação de um novo?

  • Atualização de componente existente
  • Criação de novo componente

Contexto adicional
A esquerda é como o checkbox se comporta hoje ao ser utilizado e a direita como ele deve passar a funcionar
Image


🛠️ Notas de implementação (para desenvolvedores e agentes de IA) - NÃO REMOVER

  • Utilize os links do Figma com as ferramentas Figma MCP para inspecionar e extrair os detalhes do componente antes de iniciar o desenvolvimento.
  • Todo componente implementado ou atualizado deve conter histórias no Storybook demonstrando todos os estados possíveis do componente.
  • Sempre que possível, consolide as variações em uma única página de story, utilizando controles ou variantes para representar diferentes estados.
    </issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: stencil-js-specialist
Specialized agent for creating, updating, and maintaining Blip Design System components using StencilJS, including tests, documentation, and React wrappers


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

- Add `indeterminate` property to bds-checkbox component
- Show minus icon (less) when in indeterminate state
- Clicking when indeterminate transitions to checked state
- Add styles for indeterminate and indeterminate-disabled states
- Update bdsChange event to include indeterminate property
- Add unit tests for indeterminate state behavior
- Add e2e tests for indeterminate state
- Update Storybook stories with AllStates and IndeterminateExample
- Update MDX documentation with indeterminate state info

Co-authored-by: TarsysFonseca <3694626+TarsysFonseca@users.noreply.github.com>
Copilot AI changed the title [WIP] Add intermediate state to bds-checkbox component feat(checkbox): add indeterminate state for partial selection Nov 26, 2025
Copilot AI requested a review from TarsysFonseca November 26, 2025 21:37
@TarsysFonseca
Copy link
Contributor

@copilot revise os seguintes pontos:

  • Para os exemplos de utilização hierárquica (e.g., Checkbox Pai/Filho), garantir que todos os componentes e wrappers utilizados nas histórias estejam de acordo com o Design System (utilizando tokens de espaçamento, tipografia, cores, etc., do DS).
  • Assegurar que todas as implementações existentes do Checkbox (que não passam a nova prop indeterminate) continuem funcionando perfeitamente e não sofram alterações visuais ou de comportamento.
  • O componente deve manter seu comportamento padrão (somente checked e unchecked) se a propriedade indeterminate for omitida ou definida como false.

…mpatibility tests

- Replace inline styles with bds-grid and bds-typo components
- Add backwards compatibility tests to ensure checkbox works without indeterminate prop
- Verify default checked/unchecked behavior is preserved

Co-authored-by: TarsysFonseca <3694626+TarsysFonseca@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 27, 2025

@copilot revise os seguintes pontos:

  • Para os exemplos de utilização hierárquica (e.g., Checkbox Pai/Filho), garantir que todos os componentes e wrappers utilizados nas histórias estejam de acor...

Addressed all the points in commit 1c88287:

  1. DS Components in stories: Replaced inline styles (div with style) with bds-grid and bds-typo components for proper spacing and typography tokens.

  2. Backwards compatibility: Added 3 new unit tests that verify:

    • Checkbox works without indeterminate prop (default checked/unchecked behavior)
    • Event emission includes indeterminate: false when prop is omitted
    • Visual behavior matches original when not using indeterminate
  3. Default behavior preserved: When indeterminate is omitted or false, the component maintains its original checked/unchecked toggle behavior.

checkbox-all-states

checkbox-indeterminate-example

@TarsysFonseca TarsysFonseca marked this pull request as ready for review November 28, 2025 13:01
Copilot AI review requested due to automatic review settings November 28, 2025 13:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an indeterminate state to the bds-checkbox component to support partial selection scenarios, where a parent checkbox controls multiple child checkboxes. The implementation introduces a new indeterminate boolean property that displays a minus icon and transitions to checked state when clicked.

Key Changes:

  • Added indeterminate property to the checkbox component with proper handling in all interaction methods
  • Implemented new SCSS styles for indeterminate and indeterminate-disabled states
  • Added comprehensive unit tests (36 total including 3 backwards compatibility tests) and e2e tests (10 total)

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/components/checkbox/checkbox.tsx Added indeterminate property and updated interaction logic (onClick, toggle, handleKeyDown) to transition from indeterminate to checked state
src/components/checkbox/checkbox.scss Added CSS styles for checkbox--indeterminate and checkbox--indeterminate-disabled states
src/components/checkbox/test/checkbox.spec.ts Added 36 unit tests including indeterminate state tests and backwards compatibility tests
src/components/checkbox/test/checkbox.e2e.ts Added 10 e2e tests covering indeterminate state interactions
src/components/checkbox/checkbox.stories.jsx Added AllStates and IndeterminateExample stories with interactive parent-child demo
src/components/checkbox/checkbox.mdx Updated documentation with indeterminate state examples and event details
src/components/checkbox/readme.md Auto-generated property documentation for indeterminate property
src/components.d.ts Auto-generated TypeScript definitions for the new property

@@ -101,13 +131,24 @@ export class Checkbox {

private handleKeyDown(event) {
if (event.key == 'Enter') {
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality operator (===) instead of loose equality (==) for type-safe comparison. This is a JavaScript best practice and aligns with TypeScript conventions.

Change:

if (event.key == 'Enter') {

To:

if (event.key === 'Enter') {
Suggested change
if (event.key == 'Enter') {
if (event.key === 'Enter') {

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +138
useEffect(() => {
const parentCheckbox = document.getElementById('parent-checkbox');
const childCheckboxes = [
document.getElementById('child1'),
document.getElementById('child2'),
document.getElementById('child3')
];

const updateParentState = () => {
const checkedCount = childCheckboxes.filter(cb => cb.checked).length;

if (checkedCount === 0) {
parentCheckbox.checked = false;
parentCheckbox.indeterminate = false;
} else if (checkedCount === childCheckboxes.length) {
parentCheckbox.checked = true;
parentCheckbox.indeterminate = false;
} else {
parentCheckbox.indeterminate = true;
}
};

parentCheckbox.addEventListener('bdsChange', (e) => {
// When parent is clicked (from indeterminate), it becomes checked
// and all children should be checked
const isChecked = e.detail.checked;
childCheckboxes.forEach(cb => {
cb.checked = isChecked;
});
console.log('Parent changed:', e.detail);
});

childCheckboxes.forEach(cb => {
cb.addEventListener('bdsChange', () => {
updateParentState();
});
});
});
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useEffect hook is missing a cleanup function and dependency array. This can lead to memory leaks as event listeners are not removed when the component unmounts or re-renders.

Consider adding a cleanup function and dependency array:

useEffect(() => {
  const parentCheckbox = document.getElementById('parent-checkbox');
  const childCheckboxes = [
    document.getElementById('child1'),
    document.getElementById('child2'),
    document.getElementById('child3')
  ];

  const updateParentState = () => {
    const checkedCount = childCheckboxes.filter(cb => cb.checked).length;
    
    if (checkedCount === 0) {
      parentCheckbox.checked = false;
      parentCheckbox.indeterminate = false;
    } else if (checkedCount === childCheckboxes.length) {
      parentCheckbox.checked = true;
      parentCheckbox.indeterminate = false;
    } else {
      parentCheckbox.indeterminate = true;
    }
  };

  const parentHandler = (e) => {
    const isChecked = e.detail.checked;
    childCheckboxes.forEach(cb => {
      cb.checked = isChecked;
    });
    console.log('Parent changed:', e.detail);
  };

  const childHandler = () => {
    updateParentState();
  };

  parentCheckbox.addEventListener('bdsChange', parentHandler);
  childCheckboxes.forEach(cb => {
    cb.addEventListener('bdsChange', childHandler);
  });

  // Cleanup function
  return () => {
    parentCheckbox.removeEventListener('bdsChange', parentHandler);
    childCheckboxes.forEach(cb => {
      cb.removeEventListener('bdsChange', childHandler);
    });
  };
}, []); // Empty dependency array to run only once

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://takenet.github.io/blip-ds/pr-preview/pr-1112/

Built to branch gh-pages at 2026-02-24 12:53 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bds-checkbox add intermediate state to the checkbox

3 participants