Skip to content

Conditional formatting - #2538

Draft
AndyScherzinger wants to merge 27 commits into
mainfrom
feat/360/conditional-formatting
Draft

Conditional formatting#2538
AndyScherzinger wants to merge 27 commits into
mainfrom
feat/360/conditional-formatting

Conversation

@AndyScherzinger

Copy link
Copy Markdown
Member

WIP

@AndyScherzinger AndyScherzinger added enhancement New feature or request 2. developing Work in progress labels Apr 26, 2026
@AndyScherzinger
AndyScherzinger force-pushed the feat/360/conditional-formatting branch 3 times, most recently from b43b008 to 0d4a078 Compare May 3, 2026 14:52
@AndyScherzinger
AndyScherzinger force-pushed the feat/360/conditional-formatting branch from 0d4a078 to cc24f5c Compare September 7, 2026 12:47
@AndyScherzinger
AndyScherzinger force-pushed the feat/360/conditional-formatting branch 23 times, most recently from 839c3da to 76f37dd Compare September 8, 2026 19:52
@AndyScherzinger
AndyScherzinger force-pushed the feat/360/conditional-formatting branch 2 times, most recently from cd7e6c0 to 123cec1 Compare September 9, 2026 18:11
… table

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…etion hooks

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…onse types

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
… hooks

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…ion option ID remapping

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…mutation routes for formatting API

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…onents

Assisted-by: ClaudeCode:claude-sonnet-4-6
Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…ponents

Assisted-by: ClaudeCode:claude-sonnet-4-6
Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…cement

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Adds Format rules button to table toolbar, connects FormattingManager
modal, propagates viewId through CustomTable/TableHeader for column
popovers, and evaluates formatting rules on row load and changes.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…tingRuleColMapper

Covers column deletion marking rules broken, column type change,
selection option deletion, saveForView junction rebuild, and mapper
sync/find/delete operations.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…atting UI

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Use OCP\DB\IResult instead of Doctrine\DBAL\Result for executeQuery mock
(IQueryBuilder declares that return type). Set newTable->setId(1) in
TablesMigratorTest formatting tests so tableIdMap entry is non-null and
the importView callback fires.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
- Remove unused loadTable import in e2e spec
- Remove unused showError import in formatting store
- Fix v-if/v-for on same element in ConditionGroupBuilder (use wrapper template)
- Replace self-closing <input/> with <input> on void elements (FormatStylePicker, RuleEditor, RuleSetEditor)
- Remove alignment spaces before return in switch statements (no-multi-spaces)
- Replace boolean ternary with plain expression (no-unneeded-ternary)
- Fix multiline-ternary in toCSS textDecoration
- Add parentheses around mixed && / || in hasRulesForColumn (no-mixed-operators)
- Move props above setup in TableRow and Options (vue/order-in-components)

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…faults

Move TablesFormatting* psalm-types before TablesView in ResponseDefinitions
so the forward reference to TablesFormattingRuleSet is resolved in order.
Use ['groups' => []] as default for \$condition params to match declared
array shape. Widen FormattingConditionGroupInput constructor docblock to
include optional value/values fields.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…Request::getFormat() conflict

'format' is a reserved request parameter consumed by Nextcloud's
AppFramework to determine response content type. Sending an array
as the top-level 'format' POST parameter in createRule/updateRule
caused Request::getFormat() to return array instead of ?string,
producing an HTTP 500.

Renamed controller params $format → $style (mapping back to the
internal 'format' key for FormattingRuleInput). Store actions
destructure 'format' from caller data and re-key it as 'style'
before POST/PUT so all callers remain unchanged.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Placeholder conditions created by ConditionGroupBuilder have null
columnId and operator until the user fills them in. These caused a
HTTP 400 because PHP's isset() returns false for null, triggering the
'Condition requires columnId, columnType and operator' validation.

Filter out any condition missing columnId, operator, or columnType
before posting, and drop groups that become empty after filtering.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…rect mutations

FilterEntry mutates its filterEntry prop in-place rather than emitting
update:filter-entry events, so onConditionUpdate was never called and
localCondition in RuleEditor always held the initial empty state.

Add a deep watcher on mutableGroups with a syncingFromProp guard so
any in-place mutation made by FilterEntry is still emitted upward,
while re-assignment from the conditionSet prop watcher does not trigger
a redundant re-emit loop.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The rename of the rule request parameter from 'format' to 'style' and the
['groups' => []] default for the condition parameter changed the API
contract, but the generated openapi.json and openapi.ts were not
regenerated. Regenerate both so the openapi CI check matches the code.

Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
… CRUD paths and API controller

Cover the parts of the formatting backend that had no tests:

- FormattingInputTest: every whitelisted operator, style validation,
  condition/group/rule/rule-set limits and required fields.
- FormattingServiceCrudTest: create/update/delete/reorder of rule sets and
  rules, permission and not-found errors, column ownership checks, the 50
  rule-set and 20 rule limits, junction index sync, and re-validation of
  broken rules after a rule update.
- FormattingApiControllerTest: 400 mapping for invalid input, 403/404/500
  mapping for service errors, and the 'style' request parameter being
  mapped to the internal 'format' key.

Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…ne and style picker

formattingStore.cy.js exercises the client-side evaluation through the
Pinia store: every operator of the current vocabulary including selection
option ids and date helpers, AND within a group and OR across groups, row
versus column targets, first-match versus all-matches, disabled and broken
rule sets and rules, hasRulesForColumn, re-evaluation, and the toCSS
mapping.

FormatStylePicker.cy.js covers the toggle buttons, the automatic black or
white text color derived from the background luminance, the WCAG contrast
warning once the user overrides the text color, invalid hex input and the
clear buttons.

Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…erage

The previous spec only opened the dialog; three of its five tests had no
assertions. The table, columns, view and rows are now created through the
REST API in beforeAll so the tests do not depend on the column creation UI.

API tests cover rule set creation for row and column targets, the 400 for a
missing target column, rule creation exposed through the view payload, 400s
for unknown operators and style keys, reorder persistence, rule set and
rule updates keeping their ids, 404 for unknown rules, rejection of a user
without manage rights, and rules being marked broken and disabled when a
referenced column is deleted.

UI tests create a rule set and a 'contains' rule through the dialog and
assert the row background, toggle the rule set from the column header
popover, and check the broken indicator after the column is deleted.

Assisted-by: ClaudeCode:claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01XfLgKWUTg2M1jPdqgQz3FD
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
@AndyScherzinger
AndyScherzinger force-pushed the feat/360/conditional-formatting branch from 123cec1 to 27c5a4b Compare September 10, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2. developing Work in progress AI assisted enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants