Skip to content

Components API Reference

All component base classes live in dj_design_system.components. Any new component class added to that module will appear here automatically.

dj_design_system.components

BaseComponent(**kwargs)

Source code in dj_design_system/components.py
54
55
56
57
58
59
60
def __init__(self, **kwargs):
    self.context = {}
    for var_name, var_value in kwargs.items():
        setattr(self, var_name, var_value)

    self._validate_meta_constraints()
    self.validate_params()

__init_subclass__(**kwargs)

Validate Meta constraint declarations at class definition time.

Source code in dj_design_system/components.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def __init_subclass__(cls, **kwargs) -> None:
    """Validate Meta constraint declarations at class definition time."""
    super().__init_subclass__(**kwargs)

    if is_abstract(cls):
        return

    meta = get_own_meta(cls)
    param_names = set(cls.get_params().keys())

    for a, b in getattr(meta, "mutually_exclusive", []):
        for name in (a, b):
            if name not in param_names:
                raise ValueError(
                    f"{cls.__name__}.Meta.mutually_exclusive references unknown param '{name}'."
                )

    for dependent, dependency in getattr(meta, "requires", []):
        for name in (dependent, dependency):
            if name not in param_names:
                raise ValueError(
                    f"{cls.__name__}.Meta.requires references unknown param '{name}'."
                )

docstring() classmethod

Return a string describing the API of this component, including its parameters and their types.

Source code in dj_design_system/components.py
172
173
174
175
176
177
178
179
180
181
@classmethod
def docstring(cls) -> str:
    """Return a string describing the API of this component, including its parameters and their types."""
    params = cls.get_params()
    api_docs = f"{cls.__doc__}\n\n"
    if len(params) > 0:
        api_docs += "Parameters:\n"
    for parameter_spec in params.values():
        api_docs += f"- {parameter_spec.docstring()}\n"
    return api_docs

get_app_label() classmethod

Return the app label this component was discovered in.

Source code in dj_design_system/components.py
190
191
192
193
194
195
@classmethod
def get_app_label(cls) -> str:
    """Return the app label this component was discovered in."""
    from dj_design_system import component_registry

    return component_registry.get_info(cls).app_label

get_available_themes() classmethod

Return the list of theme values supported by this component.

Source code in dj_design_system/components.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@classmethod
def get_available_themes(cls) -> list[str]:
    """Return the list of theme values supported by this component."""
    meta = get_own_meta(cls)
    available = getattr(meta, "available_themes", None)
    if isinstance(available, str):
        return [available]
    if available is not None:
        return list(available)

    app_label = cls.get_app_label()
    app_themes = (dds_settings.APP_THEMES or {}).get(app_label)
    if isinstance(app_themes, str):
        return [app_themes]
    if app_themes is not None:
        return list(app_themes)

    return [t.value for t in get_themes()]

get_classes_string()

Get a string of CSS classes based on the context.

Source code in dj_design_system/components.py
130
131
132
133
134
135
136
def get_classes_string(self):
    """Get a string of CSS classes based on the context."""
    classes = []
    for param_name, spec in self.params.items():
        param_value = getattr(self, param_name)
        classes.extend(spec.get_css_classes(param_name, param_value))
    return " ".join(classes)

get_context()

Get the context for rendering the component.

Source code in dj_design_system/components.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def get_context(self) -> dict[str, Any]:
    """Get the context for rendering the component."""
    self.context["classes"] = self.get_classes_string()

    from django.utils.html import escape

    all_attrs = []
    for param_name, spec in self.params.items():
        value = getattr(self, param_name)
        self.context[param_name] = value
        self.context.update(spec.get_extra_context(param_name, value))

        if value is not None and hasattr(spec, "get_html_attributes"):
            attr_dict = spec.get_html_attributes(param_name, value)
            if attr_dict:
                attr_strs = []
                for k, v in attr_dict.items():
                    if isinstance(v, bool):
                        attr_style = getattr(spec, "attr_style", "string")
                        if attr_style == "boolean":
                            if v:
                                attr_strs.append(f"{escape(k)}")
                        else:
                            attr_strs.append(f'{escape(k)}="{str(v).lower()}"')
                    else:
                        attr_strs.append(f'{escape(k)}="{escape(str(v))}"')

                param_attr_str = " ".join(attr_strs)
                if param_attr_str:
                    self.context[f"{param_name}_attr"] = mark_safe(param_attr_str)
                    all_attrs.append(param_attr_str)

    self.context["attrs"] = mark_safe(" ".join(all_attrs))
    return self.context

get_media() classmethod

Return the CSS and JS static URL paths required by this component.

Source code in dj_design_system/components.py
204
205
206
207
208
209
@classmethod
def get_media(cls) -> "ComponentMedia":
    """Return the CSS and JS static URL paths required by this component."""
    from dj_design_system import component_registry

    return component_registry.get_info(cls).media

get_name() classmethod

Return the component's registered name from the registry.

Source code in dj_design_system/components.py
183
184
185
186
187
188
@classmethod
def get_name(cls) -> str:
    """Return the component's registered name from the registry."""
    from dj_design_system import component_registry

    return component_registry.get_info(cls).name

get_params() classmethod

Get the parameters for this component, cached per class.

Source code in dj_design_system/components.py
159
160
161
162
163
164
165
166
167
168
169
170
@classmethod
def get_params(cls) -> dict[str, "BaseParam"]:
    """Get the parameters for this component, cached per class."""
    cached: dict[str, BaseParam] | None = cls.__dict__.get("_cached_params")
    if cached is None:
        cached = {}
        for klass in cls.__mro__:
            for attr_name, attr_value in vars(klass).items():
                if isinstance(attr_value, BaseParam) and attr_name not in cached:
                    cached[attr_name] = attr_value
        setattr(cls, "_cached_params", cached)
    return dict(cached)

get_positional_args() classmethod

Return the list of positional arg names from the class's own Meta.positional_args.

Source code in dj_design_system/components.py
211
212
213
214
215
216
@classmethod
def get_positional_args(cls) -> list[str]:
    """Return the list of positional arg names from the class's own Meta.positional_args."""
    meta = get_own_meta(cls)
    positional = getattr(meta, "positional_args", None)
    return list(positional) if positional else []

get_relative_path() classmethod

Return the relative path within the app's components directory.

Source code in dj_design_system/components.py
197
198
199
200
201
202
@classmethod
def get_relative_path(cls) -> str:
    """Return the relative path within the app's components directory."""
    from dj_design_system import component_registry

    return component_registry.get_info(cls).relative_path

map_positional_args(positional_args, args, kwargs) staticmethod

Map positional arguments to keyword arguments using the positional_args spec.

Source code in dj_design_system/components.py
237
238
239
240
241
242
243
244
245
@staticmethod
def map_positional_args(
    positional_args: list[str], args: tuple, kwargs: dict
) -> dict:
    """Map positional arguments to keyword arguments using the positional_args spec."""
    for i, arg_name in enumerate(positional_args):
        if i < len(args):
            kwargs[arg_name] = args[i]
    return kwargs

param_has_been_set(param_name)

Return True if the parameter has been explicitly set on this component instance.

Source code in dj_design_system/components.py
66
67
68
69
70
71
def param_has_been_set(self, param_name: str) -> bool:
    """Return True if the parameter has been explicitly set on this component instance."""
    param = self.params.get(param_name)
    if param is None:
        raise ValueError(f"Unknown param '{param_name}'")
    return param.has_been_set(self)

render()

Render the component as an HTML string.

Source code in dj_design_system/components.py
138
139
140
141
142
143
def render(self) -> str:
    """Render the component as an HTML string."""
    template_name: str | None = getattr(type(self), "_template_name", None)
    if template_name:
        return mark_safe(render_to_string(template_name, self.get_context()))
    return format_html(format_string=self.template_format_str, **self.get_context())

validate_params()

An override hook allowing param combinations or values to raise exceptions if necessary

Source code in dj_design_system/components.py
62
63
64
def validate_params(self) -> None:
    """An override hook allowing param combinations or values to raise exceptions if necessary"""
    ...

BlockComponent(content=None, *, slots=None, **kwargs)

Bases: BaseComponent

A component registered as a Django simple_block_tag.

Source code in dj_design_system/components.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
def __init__(
    self,
    content: SafeString | None = None,
    *,
    slots: dict[str, SafeString] | None = None,
    **kwargs,
):
    if self.has_slots():
        if slots is None:
            slots = {}
        tag_name = get_meta_name(type(self)) or derive_name(type(self))
        self.slots = validate_slots(self.get_slots(), slots, tag_name)
        self.content = None
    else:
        self.content = content
        self.slots = {}
    super().__init__(**kwargs)

as_tag() classmethod

Return a template tag function or compilation function.

Source code in dj_design_system/components.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
@classmethod
def as_tag(cls):
    """Return a template tag function or compilation function."""
    if cls.has_slots():
        tag_name = get_meta_name(cls) or derive_name(cls)
        return make_slotted_block_tag(cls, tag_name)

    positional_args = cls.get_positional_args()

    def _tag(content, *args, **kwargs):
        cls.map_positional_args(positional_args, args, kwargs)
        return cls(content=content, **kwargs)

    return _tag

get_context()

Add content or slot values to the context automatically.

Source code in dj_design_system/components.py
303
304
305
306
307
308
309
310
311
def get_context(self) -> dict[str, Any]:
    """Add ``content`` or slot values to the context automatically."""
    context = super().get_context()
    if self.has_slots():
        for name, value in self.slots.items():
            context[name] = value
    else:
        context["content"] = self.content
    return context

get_slots() classmethod

Return the declared slots dict from Meta, or empty dict.

Source code in dj_design_system/components.py
319
320
321
322
323
@classmethod
def get_slots(cls) -> dict[str, "Slot"]:
    """Return the declared slots dict from Meta, or empty dict."""
    meta = get_own_meta(cls)
    return dict(getattr(meta, "slots", {}))

has_slots() classmethod

Return True if this component declares named slots via Meta.slots.

Source code in dj_design_system/components.py
313
314
315
316
317
@classmethod
def has_slots(cls) -> bool:
    """Return True if this component declares named slots via Meta.slots."""
    meta = get_own_meta(cls)
    return bool(getattr(meta, "slots", None))

TagComponent(**kwargs)

Bases: BaseComponent

A component registered as a Django simple_tag.

Source code in dj_design_system/components.py
54
55
56
57
58
59
60
def __init__(self, **kwargs):
    self.context = {}
    for var_name, var_value in kwargs.items():
        setattr(self, var_name, var_value)

    self._validate_meta_constraints()
    self.validate_params()

as_tag() classmethod

Return a template tag function mapping positional args via Meta.positional_args.

Source code in dj_design_system/components.py
265
266
267
268
269
270
271
272
273
274
@classmethod
def as_tag(cls):
    """Return a template tag function mapping positional args via Meta.positional_args."""
    positional_args = cls.get_positional_args()

    def _tag(*args, **kwargs):
        cls.map_positional_args(positional_args, args, kwargs)
        return cls(**kwargs)

    return _tag

options: members: true show_root_heading: false show_source: true show_symbol_type_heading: true show_symbol_type_toc: true docstring_style: google merge_init_into_class: true group_by_category: true


Slots API

dj_design_system.slots

Named slot support for BlockComponent.

A slot defines a named content area that template authors fill using {% slot "name" %}...{% endslot %} tags inside a block component.

Slot(required=False, default='', description='')

Declaration of a named content slot on a BlockComponent.

Parameters:

Name Type Description Default
required bool

Whether the slot must be provided. Defaults to False.

False
default str

Default content when the slot is not provided. Only meaningful when required=False.

''
description str

Human-readable description for documentation/gallery.

''
Source code in dj_design_system/slots.py
25
26
27
28
29
30
31
32
33
def __init__(
    self,
    required: bool = False,
    default: str = "",
    description: str = "",
) -> None:
    self.required = required
    self.default = default
    self.description = description

validate_slots(declared_slots, provided_slots, component_name)

Validate provided slots against a component's declared slots.

Returns a complete dict of slot values (filling in defaults for missing optional slots).

Raises:

Type Description
ValueError

If a required slot is missing, an unknown slot name is used, or a slot name appears more than once.

Source code in dj_design_system/slots.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def validate_slots(
    declared_slots: dict[str, Slot],
    provided_slots: dict[str, str],
    component_name: str,
) -> dict[str, str]:
    """Validate provided slots against a component's declared slots.

    Returns a complete dict of slot values (filling in defaults for
    missing optional slots).

    Raises:
        ValueError: If a required slot is missing, an unknown slot name
            is used, or a slot name appears more than once.
    """
    # Check for unknown slot names
    unknown = set(provided_slots) - set(declared_slots)
    if unknown:
        names = ", ".join(sorted(unknown))
        valid = ", ".join(sorted(declared_slots))
        raise ValueError(
            f"'{component_name}' received unknown slot(s): {names}. "
            f"Valid slots are: {valid}."
        )

    # Build result with defaults for missing optional slots
    result: dict[str, str] = {}
    for name, slot in declared_slots.items():
        if name in provided_slots:
            result[name] = provided_slots[name]
        elif slot.required:
            valid = ", ".join(sorted(declared_slots))
            raise ValueError(
                f"'{component_name}' requires slot '{name}' but it was not provided. "
                f"Declared slots: {valid}."
            )
        else:
            result[name] = slot.default

    return result

options: members: true show_root_heading: false show_source: true show_symbol_type_heading: true show_symbol_type_toc: true docstring_style: google merge_init_into_class: true