@@ -23,6 +23,11 @@ import {
2323 SIZES as buttonSizes ,
2424 STYLES as buttonStyles ,
2525} from './Button.stories' ;
26+ import {
27+ getSecondaryButtonStyle ,
28+ getSecondaryButtonHoverStyles ,
29+ } from './index' ;
30+ import type { SupersetTheme } from '@apache-superset/core/theme' ;
2631
2732test ( 'works with an onClick handler' , ( ) => {
2833 const mockAction = jest . fn ( ) ;
@@ -47,3 +52,165 @@ test('All the sorybook gallery variants mount', () => {
4752
4853 expect ( getAllByRole ( 'button' ) ) . toHaveLength ( permutationCount ) ;
4954} ) ;
55+
56+ test ( 'secondary button renders without errors' , ( ) => {
57+ const { getByRole } = render (
58+ < Button buttonStyle = "secondary" > Secondary</ Button > ,
59+ ) ;
60+ expect ( getByRole ( 'button' ) ) . toBeInTheDocument ( ) ;
61+ } ) ;
62+
63+ test ( 'getSecondaryButtonStyle uses fallback tokens when custom tokens not set' , ( ) => {
64+ const mockTheme = {
65+ colorPrimary : '#2893B3' ,
66+ colorPrimaryBg : '#e6f4f7' ,
67+ colorPrimaryBgHover : '#cce9ef' ,
68+ colorPrimaryBorder : '#99d3df' ,
69+ } as SupersetTheme ;
70+
71+ const styles = getSecondaryButtonStyle ( mockTheme ) ;
72+
73+ // Default state uses inline styles (no !important needed)
74+ expect ( styles . color ) . toBe ( '#2893B3' ) ;
75+ expect ( styles . backgroundColor ) . toBe ( '#e6f4f7' ) ;
76+ expect ( styles . borderColor ) . toBe ( 'transparent' ) ;
77+ } ) ;
78+
79+ test ( 'getSecondaryButtonHoverStyles uses fallback tokens when custom tokens not set' , ( ) => {
80+ const mockTheme = {
81+ colorPrimary : '#2893B3' ,
82+ colorPrimaryBg : '#e6f4f7' ,
83+ colorPrimaryBgHover : '#cce9ef' ,
84+ colorPrimaryBorder : '#99d3df' ,
85+ } as SupersetTheme ;
86+
87+ const hoverStyles = getSecondaryButtonHoverStyles ( mockTheme ) ;
88+
89+ // Hover/active states use CSS with !important for specificity
90+ expect ( hoverStyles [ '&:hover' ] . backgroundColor ) . toBe ( '#cce9ef !important' ) ;
91+ expect ( hoverStyles [ '&:active' ] . backgroundColor ) . toBe ( '#99d3df !important' ) ;
92+ } ) ;
93+
94+ test ( 'getSecondaryButtonStyle uses custom tokens when provided' , ( ) => {
95+ const mockTheme = {
96+ colorPrimary : '#2893B3' ,
97+ colorPrimaryBg : '#e6f4f7' ,
98+ colorPrimaryBgHover : '#cce9ef' ,
99+ colorPrimaryBorder : '#99d3df' ,
100+ // Custom secondary button tokens
101+ buttonSecondaryColor : '#custom-color' ,
102+ buttonSecondaryBg : '#custom-bg' ,
103+ buttonSecondaryBorderColor : '#custom-border' ,
104+ } as SupersetTheme ;
105+
106+ const styles = getSecondaryButtonStyle ( mockTheme ) ;
107+
108+ // Default state uses inline styles (no !important needed)
109+ expect ( styles . color ) . toBe ( '#custom-color' ) ;
110+ expect ( styles . backgroundColor ) . toBe ( '#custom-bg' ) ;
111+ expect ( styles . borderColor ) . toBe ( '#custom-border' ) ;
112+ } ) ;
113+
114+ test ( 'getSecondaryButtonHoverStyles uses custom tokens when provided' , ( ) => {
115+ const mockTheme = {
116+ colorPrimary : '#2893B3' ,
117+ colorPrimaryBg : '#e6f4f7' ,
118+ colorPrimaryBgHover : '#cce9ef' ,
119+ colorPrimaryBorder : '#99d3df' ,
120+ // Custom secondary button tokens
121+ buttonSecondaryHoverColor : '#custom-hover-color' ,
122+ buttonSecondaryHoverBg : '#custom-hover-bg' ,
123+ buttonSecondaryHoverBorderColor : '#custom-hover-border' ,
124+ buttonSecondaryActiveColor : '#custom-active-color' ,
125+ buttonSecondaryActiveBg : '#custom-active-bg' ,
126+ buttonSecondaryActiveBorderColor : '#custom-active-border' ,
127+ } as SupersetTheme ;
128+
129+ const hoverStyles = getSecondaryButtonHoverStyles ( mockTheme ) ;
130+
131+ // Hover/active states use CSS with !important for specificity
132+ expect ( hoverStyles [ '&:hover' ] . color ) . toBe ( '#custom-hover-color !important' ) ;
133+ expect ( hoverStyles [ '&:hover' ] . backgroundColor ) . toBe (
134+ '#custom-hover-bg !important' ,
135+ ) ;
136+ expect ( hoverStyles [ '&:hover' ] . borderColor ) . toBe (
137+ '#custom-hover-border !important' ,
138+ ) ;
139+ expect ( hoverStyles [ '&:active' ] . color ) . toBe ( '#custom-active-color !important' ) ;
140+ expect ( hoverStyles [ '&:active' ] . backgroundColor ) . toBe (
141+ '#custom-active-bg !important' ,
142+ ) ;
143+ expect ( hoverStyles [ '&:active' ] . borderColor ) . toBe (
144+ '#custom-active-border !important' ,
145+ ) ;
146+ } ) ;
147+
148+ test ( 'getSecondaryButtonStyle supports partial token overrides' , ( ) => {
149+ const mockTheme = {
150+ colorPrimary : '#2893B3' ,
151+ colorPrimaryBg : '#e6f4f7' ,
152+ colorPrimaryBgHover : '#cce9ef' ,
153+ colorPrimaryBorder : '#99d3df' ,
154+ // Only override some tokens
155+ buttonSecondaryBg : '#custom-bg' ,
156+ buttonSecondaryBorderColor : '#custom-border' ,
157+ } as SupersetTheme ;
158+
159+ const styles = getSecondaryButtonStyle ( mockTheme ) ;
160+
161+ // Should use custom values where provided (no !important for inline styles)
162+ expect ( styles . backgroundColor ) . toBe ( '#custom-bg' ) ;
163+ expect ( styles . borderColor ) . toBe ( '#custom-border' ) ;
164+ // Should fallback to Ant Design tokens where not provided
165+ expect ( styles . color ) . toBe ( '#2893B3' ) ;
166+ } ) ;
167+
168+ test ( 'getSecondaryButtonHoverStyles supports partial token overrides' , ( ) => {
169+ const mockTheme = {
170+ colorPrimary : '#2893B3' ,
171+ colorPrimaryBg : '#e6f4f7' ,
172+ colorPrimaryBgHover : '#cce9ef' ,
173+ colorPrimaryBorder : '#99d3df' ,
174+ // Only override hover bg
175+ buttonSecondaryHoverBg : '#custom-hover-bg' ,
176+ } as SupersetTheme ;
177+
178+ const hoverStyles = getSecondaryButtonHoverStyles ( mockTheme ) ;
179+
180+ // Should use custom value where provided
181+ expect ( hoverStyles [ '&:hover' ] . backgroundColor ) . toBe (
182+ '#custom-hover-bg !important' ,
183+ ) ;
184+ // Should fallback to Ant Design tokens where not provided
185+ expect ( hoverStyles [ '&:hover' ] . color ) . toBe ( '#2893B3 !important' ) ;
186+ expect ( hoverStyles [ '&:active' ] . backgroundColor ) . toBe ( '#99d3df !important' ) ;
187+ } ) ;
188+
189+ test ( 'getSecondaryButtonStyle falls back when tokens are empty strings' , ( ) => {
190+ const mockTheme = {
191+ colorPrimary : '#2893B3' ,
192+ colorPrimaryBg : '#e6f4f7' ,
193+ buttonSecondaryColor : '' ,
194+ buttonSecondaryBg : '' ,
195+ buttonSecondaryBorderColor : '' ,
196+ } as SupersetTheme ;
197+
198+ const styles = getSecondaryButtonStyle ( mockTheme ) ;
199+
200+ // Empty strings should trigger fallback to primary tokens
201+ expect ( styles . color ) . toBe ( '#2893B3' ) ;
202+ expect ( styles . backgroundColor ) . toBe ( '#e6f4f7' ) ;
203+ expect ( styles . borderColor ) . toBe ( 'transparent' ) ;
204+ } ) ;
205+
206+ test ( 'secondary button merges consumer style with theme styles' , ( ) => {
207+ const { getByRole } = render (
208+ < Button buttonStyle = "secondary" style = { { marginTop : 10 , padding : 20 } } >
209+ Styled Secondary
210+ </ Button > ,
211+ ) ;
212+ const button = getByRole ( 'button' ) ;
213+
214+ // Consumer styles should be applied
215+ expect ( button ) . toHaveStyle ( { marginTop : '10px' , padding : '20px' } ) ;
216+ } ) ;
0 commit comments